Project import
diff --git a/common/MODULE_LICENSE_APACHE2 b/common/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/common/MODULE_LICENSE_APACHE2
diff --git a/common/NOTICE b/common/NOTICE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/common/NOTICE
@@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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.
diff --git a/common/brillo_camera/Android.mk b/common/brillo_camera/Android.mk new file mode 100644 index 0000000..950ed33 --- /dev/null +++ b/common/brillo_camera/Android.mk
@@ -0,0 +1,33 @@ +# Copyright 2016 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 := brillo_camera_client +LOCAL_CFLAGS := -Wall -Werror +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libbrillo \ + libbrillo-binder \ + libcamera_client \ + libcamera_metadata \ + libchrome \ + libcutils \ + libgui \ + libutils +LOCAL_SRC_FILES := \ + main.cpp +include $(BUILD_EXECUTABLE)
diff --git a/common/brillo_camera/main.cpp b/common/brillo_camera/main.cpp new file mode 100644 index 0000000..6d5a3f8 --- /dev/null +++ b/common/brillo_camera/main.cpp
@@ -0,0 +1,233 @@ +/* + * Copyright 2016 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 <iostream> +#include <string> + +#include <base/at_exit.h> +#include <base/bind.h> +#include <base/logging.h> +#include <binder/IServiceManager.h> +#include <brillo/binder_watcher.h> +#include <brillo/message_loops/base_message_loop.h> +#include <brillo/syslog_logging.h> +#include <camera/CameraMetadata.h> +#include <camera/ICameraService.h> +#include <camera/camera2/CaptureRequest.h> +#include <camera/camera2/ICameraDeviceCallbacks.h> +#include <camera/camera2/ICameraDeviceUser.h> +#include <camera/camera2/OutputConfiguration.h> +#include <gui/BufferQueue.h> +#include <gui/BufferQueueConsumer.h> +#include <gui/BufferQueueCore.h> +#include <gui/BufferQueueProducer.h> +#include <gui/CpuConsumer.h> +#include <gui/Surface.h> +#include <hardware/camera3.h> +#include <system/camera_metadata.h> +#include <utils/String16.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + +using android::BnCameraDeviceCallbacks; +using android::BufferQueue; +using android::CameraMetadata; +using android::CaptureRequest; +using android::CaptureResultExtras; +using android::CpuConsumer; +using android::ICameraDeviceCallbacks; +using android::ICameraDeviceUser; +using android::ICameraService; +using android::IGraphicBufferConsumer; +using android::IGraphicBufferProducer; +using android::OK; +using android::String16; +using android::Surface; +using android::sp; + +namespace { + +// TODO(wiley) This constant should probably come out that client library, but +// where is not obvious. +const char kCameraServiceName[] = "media.camera"; +// TODO(wiley) This happens to be a format supported by both gralloc.default and +// the golfish camera HAL, but I have no idea how to get this into +// an actual viewable image. +const int kHalPixelFormat = HAL_PIXEL_FORMAT_RAW16; + +class CameraDeviceCallbacks : public BnCameraDeviceCallbacks { + public: + explicit CameraDeviceCallbacks(brillo::MessageLoop* loop) : loop_(loop) {} + + void onDeviceError(ICameraDeviceCallbacks::CameraErrorCode error_code, + const CaptureResultExtras& /* result_extras */) override { + LOG(ERROR) << "Received camera error = " << error_code; + loop_->BreakLoop(); + } + + void onDeviceIdle() override { + LOG(INFO) << "Camera device is idle"; + } + + void onCaptureStarted( + const CaptureResultExtras& /* result_extras */, + int64_t /* timestamp */) override { + LOG(INFO) << "Capture started."; + } + + void onResultReceived( + const CameraMetadata& /* metadata */, + const CaptureResultExtras& /* result_extras */) override { + LOG(INFO) << "Capture result received"; + result_received_ = true; + loop_->BreakLoop(); + } + + void onPrepared(int stream_id) override { + LOG(INFO) << "Camera is prepared for stream " << stream_id; + } + + bool result_received() { return result_received_; } + + private: + brillo::MessageLoop* loop_ = nullptr; + bool result_received_ = false; +}; + +} // namespace + + +int main() { + base::AtExitManager at_exit_manager; + brillo::InitLog(brillo::kLogToStderr); + + // Create a message loop. + brillo::BaseMessageLoop message_loop; + + // Initialize a binder watcher. + brillo::BinderWatcher watcher(&message_loop); + watcher.Init(); + + LOG(INFO) << "Retrieving a binder for the camera service."; + sp<ICameraService> camera_service; + android::status_t status = android::getService( + String16(kCameraServiceName), &camera_service); + CHECK(status == OK) + << "Failed to get ICameraService binder from service manager!"; + + LOG(INFO) << "Asking how many cameras we have."; + const int32_t num_cameras = camera_service->getNumberOfCameras( + ICameraService::CAMERA_TYPE_ALL); + CHECK(num_cameras > 0) + << "ICameraService reports no cameras available"; + + const int camera_id = 0; + LOG(INFO) << "Connecting to camera " << camera_id; + sp<CameraDeviceCallbacks> camera_cb = + new CameraDeviceCallbacks(&message_loop); + sp<ICameraDeviceCallbacks> camera_cb_alias = camera_cb; + const String16 client_package_name("brillo"); + sp<ICameraDeviceUser> camera_device_user; + status = camera_service->connectDevice( + camera_cb_alias, camera_id, client_package_name, + ICameraService::USE_CALLING_UID, + camera_device_user); + CHECK(status == OK) << "Failed to connect to camera id=" << camera_id + << " error=" << status; + + LOG(INFO) << "Obtaining camera info"; + CameraMetadata camera_metadata; + status = camera_device_user->getCameraInfo(&camera_metadata); + CHECK(status == OK) << "Failed to get camera info, error=" << status; + + LOG(INFO) << "Calculating smallest stream configuration"; + camera_metadata_entry streamConfigs = + camera_metadata.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + int32_t width = std::numeric_limits<int32_t>::max(); + int32_t height = 1; + for (size_t i = 0; i < streamConfigs.count; i += 4) { + int32_t fmt = streamConfigs.data.i32[i]; + int32_t inout = streamConfigs.data.i32[i + 3]; + if (fmt == ANDROID_SCALER_AVAILABLE_FORMATS_RAW16 && + inout == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) { + int32_t w = streamConfigs.data.i32[i + 1]; + int32_t h = streamConfigs.data.i32[i + 2]; + + if (width * height > w * h) { + width = w; + height = h; + } + } + } + LOG(INFO) << "width = " << width << " height = " << height; + CHECK(width != std::numeric_limits<int32_t>::max()) + << "Unable to configure stream dimensions"; + + LOG(INFO) << "Obtaining buffer queue"; + sp<IGraphicBufferProducer> buffer_producer = nullptr; + sp<IGraphicBufferConsumer> buffer_consumer = nullptr; + BufferQueue::createBufferQueue(&buffer_producer, &buffer_consumer); + CHECK(buffer_producer.get() != nullptr && buffer_consumer.get() != nullptr); + + LOG(INFO) << "Creating buffer consumer"; + sp<CpuConsumer> consumer = new CpuConsumer( + buffer_consumer, 1 /* one frame only */, true /* controlled by app */); + consumer->setDefaultBufferSize(width, height); + consumer->setDefaultBufferFormat(kHalPixelFormat); + + LOG(INFO) << "Configuring the camera"; + status = camera_device_user->beginConfigure(); + CHECK(status == OK) << "Failed calling ICameraDeviceUser::beginConfigure()" + << " error = " << status; + android::OutputConfiguration output_configuration( + buffer_producer, CAMERA3_STREAM_ROTATION_0); + status = camera_device_user->createStream(output_configuration); + CHECK(status == OK) << "Failed calling ICameraDeviceUser::createStream()" + << " error = " << status; + status = camera_device_user->endConfigure(false /* isConstrainedHighSpeed */); + CHECK(status == OK) << "Failed calling ICameraDeviceUser::endConfigure()" + << " error = " << status; + + LOG(INFO) << "Creating capture_request"; + sp<CaptureRequest> capture_request = new CaptureRequest; + status = camera_device_user->createDefaultRequest( + CAMERA3_TEMPLATE_STILL_CAPTURE, &capture_request->mMetadata); + sp<Surface> surface = new Surface( + buffer_producer, true /* controlled by app */); + capture_request->mSurfaceList.push_back(surface); + capture_request->mIsReprocess = false; + + LOG(INFO) << "Submitting capture request"; + int64_t last_frame_number = 0; + status = camera_device_user->submitRequest( + capture_request, false, &last_frame_number); + CHECK(status == OK) << "Got error=" << status + << " while submitting capture request."; + + LOG(INFO) << "Waiting for the camera to take a picture."; + // If we don't hear back from the camera for long enough, just time out. + message_loop.PostDelayedTask( + base::Bind(&brillo::BaseMessageLoop::BreakLoop, + base::Unretained(&message_loop)), + base::TimeDelta::FromSeconds(30)); + message_loop.Run(); // We'll exit on a callback from the camera. + + CHECK(camera_cb->result_received()); + + // TODO(wiley) render this image to a file to save somewhere. + + return 0; +}
diff --git a/common/brillo_gpios/AndroidProducts.mk b/common/brillo_gpios/AndroidProducts.mk new file mode 100644 index 0000000..e31e144 --- /dev/null +++ b/common/brillo_gpios/AndroidProducts.mk
@@ -0,0 +1,16 @@ +# +# 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. +# +PRODUCT_MAKEFILES := $(LOCAL_DIR)/brillo_gpios.mk
diff --git a/common/brillo_gpios/README b/common/brillo_gpios/README new file mode 100644 index 0000000..be80916 --- /dev/null +++ b/common/brillo_gpios/README
@@ -0,0 +1,43 @@ +README for Brillo GPIO sample + +This GPIO sample demonstrates how to set up GPIO and write/read to GPIO pins. +Specifically this app does the following. + + - Export an input and output GPIO pins + - Set output e.g. pin 24 direction to "out" and value to 1 + - Set input e.g. pin 28 direction to "in" and edge to "both" + - Read input pin by polling + - Once input pin is triggered to input value 1, set output value to 0 + +Once built, you can find the binary inside your tree at: + +out/target/product/dragonboard/symbols/system/bin/gpio_playground + +Do the following to deploy the sample: + - adb root + - adb remount + - adb sync + +Hardware setup: + - You need to connect a GPIO pin as output to an LED + - You need to connect a GPIO pin as input to a push button switch + +Do the following to run the sample on Dragonboard and check logs for info: + - Open a shell window and run: adb logcat + - Open another shell window and run: + - Default input pin 28 and output pin 24 + - adb shell '/system/bin/gpio_playground &' + - Choose your own output and input pins + - adb shell '/system/bin/gpio_playground --gin=<input> --gout=<output> &' + - For example, adb shell '/system/bin/gpio_playground --gin=28 --gout=24 &' + - Observe the logging info from the first window + - Observe that the LED of pin 24 is turned on + - Now push the switch of pin 28 and see that the LED gets turned off + +Notes: + - There is a GPIO port offset defined by const kDefaultGpioOffset that depends + on what kind of boards you use. For Dragonboard it is 902. This offset can + also be configured by a commandline parameter. + - There is an output pin and input pin defined by const kDefaultGpioOut and + kDefaultGpioIn respectively. They can both be configured by commandline + parameters.
diff --git a/common/brillo_gpios/brillo_gpios.mk b/common/brillo_gpios/brillo_gpios.mk new file mode 100644 index 0000000..2c92214 --- /dev/null +++ b/common/brillo_gpios/brillo_gpios.mk
@@ -0,0 +1,22 @@ +# +# 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. +# +$(call inherit-product, device/generic/brillo/brillo_base.mk) + +PRODUCT_NAME := brillo_gpios +PRODUCT_BRAND := Brillo + +PRODUCT_DEVICE := dragonboard +PRODUCT_PACKAGES += gpio_playground
diff --git a/common/brillo_gpios/src/gpio_playground/Android.mk b/common/brillo_gpios/src/gpio_playground/Android.mk new file mode 100644 index 0000000..cf4ca71 --- /dev/null +++ b/common/brillo_gpios/src/gpio_playground/Android.mk
@@ -0,0 +1,24 @@ +# +# 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. +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := gpio_playground +LOCAL_SRC_FILES := gpio_playground.cpp +LOCAL_SHARED_LIBRARIES := libchrome libbrillo libbrillo-stream +LOCAL_C_INCLUDES := external/gtest/include +LOCAL_CFLAGS := -Wall -Werror -Wno-error=unused-parameter +include $(BUILD_EXECUTABLE)
diff --git a/common/brillo_gpios/src/gpio_playground/gpio_playground.cpp b/common/brillo_gpios/src/gpio_playground/gpio_playground.cpp new file mode 100644 index 0000000..2e6319d --- /dev/null +++ b/common/brillo_gpios/src/gpio_playground/gpio_playground.cpp
@@ -0,0 +1,212 @@ +/* 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 <string> +#include <sys/poll.h> +#include <sys/signalfd.h> +#include <sys/timerfd.h> + +#include <base/files/file_path.h> +#include <base/format_macros.h> +#include <base/logging.h> +#include <base/strings/stringprintf.h> +#include <base/strings/string_number_conversions.h> +#include <base/strings/string_util.h> + +#include <brillo/flag_helper.h> +#include <brillo/streams/file_stream.h> +#include <brillo/streams/stream_utils.h> + +// Define defaults that work on the Dragonboard 410c. +const int kDefaultGpioPortOffset = 902; +const int kDefaultGpioOut = 24; +const int kDefaultGpioIn = 28; + +// GPIO sysfs path +const char* const kGPIOSysfsPath = "/sys/class/gpio"; +// GPIO polling timeout in milliseconds +const int kPollTimeoutMsecs = 3000; /* 3 seconds */ +const int kBufSize = 10; + +namespace { + +/* + * Get a file stream for GPIO export + * bool write: access mode with true for write + * return: file stream pointer + */ +brillo::StreamPtr GetGPIOExportStream(bool write) { + std::string gpio_path; + gpio_path = base::StringPrintf("%s/export", kGPIOSysfsPath); + base::FilePath dev_path{gpio_path}; + auto access_mode = brillo::stream_utils::MakeAccessMode(!write, write); + return brillo::FileStream::Open( + dev_path, access_mode, brillo::FileStream::Disposition::OPEN_EXISTING, + nullptr); +} + +/* + * Get a file stream for GPIO data + * int gpio: GPIO pin number + * std::string type: GPIO type e.g. direction, edge, value + * bool write: access mode with true for write + * return: file stream pointer + */ +brillo::StreamPtr GetGPIODataStream(int gpio, const std::string type, bool write) { + std::string gpio_path; + gpio_path = base::StringPrintf("%s/gpio%d/%s", kGPIOSysfsPath, gpio, type.c_str()); + base::FilePath dev_path{gpio_path}; + auto access_mode = brillo::stream_utils::MakeAccessMode(!write, write); + return brillo::FileStream::Open( + dev_path, access_mode, brillo::FileStream::Disposition::OPEN_EXISTING, + nullptr); +} + +} // anonymous namespace + +/* + * Export GPIO to user space + * int gpio: GPIO pin number + */ +void export_gpio(int gpio) { + + brillo::StreamPtr stream = GetGPIOExportStream(true); + if (!stream) + return; + + std::string value = base::StringPrintf("%d", gpio); + stream->WriteAllBlocking(value.data(), value.size(), nullptr); +} + +/* + * Write to GPIO + * std::string type: what GPIO type i.e. direction, value, edge + * std::string v: value to be written + * return: true + */ +bool write_gpio(int gpio, const std::string type, std::string v) { + + brillo::StreamPtr stream = GetGPIODataStream(gpio, type, true); + if (!stream) + return false; + + stream->WriteAllBlocking(v.data(), v.size(), nullptr); + return true; +} + +/* + * This method polls GPIO pin for 0 -> 1 transition. + * int gpio: GPIO pin to be polled. + * return: + * true if polling reads back successfully + * false if GPIO open error or polling error + */ +bool poll_gpio(int gpio) { + char buf[kBufSize]; + struct pollfd fdset[1]; + int nfds = 1; + int fd = -1; + int rc = -1; + int len = -1; + + std::string gpio_path; + gpio_path = base::StringPrintf("%s/gpio%d/value", kGPIOSysfsPath, gpio); + fd = open(gpio_path.c_str(), O_RDONLY | O_NONBLOCK ); + if (fd < 0) { + PLOG(ERROR) << "GPIO open error"; + return false; + } + + // Use this sawZeroValue to ignore the first spurious interrupt. + bool sawZeroValue = false; + + while(1) { + memset((void*)fdset, 0, sizeof(fdset)); + fdset[0].fd = fd; + fdset[0].events = POLLPRI; + + rc = poll(fdset, nfds, kPollTimeoutMsecs); + if (rc < 0) { + PLOG(ERROR) << "poll() failed!"; + close(fd); + return false; + } else if (rc == 0) { + LOG(INFO) << "polling..."; + } else if (fdset[0].revents & POLLPRI) { + len = read(fdset[0].fd, buf, kBufSize - 1); + lseek(fdset[0].fd, 0, SEEK_SET); + if (len >= 0) { + buf[len] = '\0'; + } + // TODO: http://b/25317933 Handle the case where read() + // might result in multiple events being read. + if (buf[0] == '0') { + LOG(INFO) << "First interrupt with value 0 ignored."; + sawZeroValue = true; + continue; + } + if (sawZeroValue && buf[0] == '1') { + LOG(INFO) << "GPIO pin reset and interrupt with value 1 caught."; + close(fd); + return true; + } + } + } + close(fd); + return true; +} + +int main(int argc, char **argv) { + int gpio_out = -1; + int gpio_in = -1; + int goffset = kDefaultGpioPortOffset; + + DEFINE_int32(gin, 28, "GPIO input"); + DEFINE_int32(gout, 24, "GPIO output"); + DEFINE_int32(goffset, 902, "GPIO port offset for Dragonboard"); + brillo::FlagHelper::Init(argc, argv, "GPIO Playground."); + + if (argc == 1 || argc == 3 || argc == 4) { + if (FLAGS_goffset) { + goffset = FLAGS_goffset; + } + gpio_out = FLAGS_gout + goffset; + gpio_in = FLAGS_gin + goffset; + } else { + printf("Pass input & output GPIO pin as follows\n"); + printf("adb shell '/system/bin/gpio_playground --gin=28 --gout=24 --goffset=902 &'\n"); + return -1; + } + + LOG(INFO) << "Export pin " << kDefaultGpioOut << " to userspace"; + export_gpio(gpio_out); + LOG(INFO) << "Set pin " << kDefaultGpioOut << " direction to out"; + write_gpio(gpio_out, "direction", "out"); + LOG(INFO) << "Set pin " << kDefaultGpioOut << " value to 1"; + write_gpio(gpio_out, "value", "1"); + + LOG(INFO) << "Export pin " << kDefaultGpioIn << " to userspace"; + export_gpio(gpio_in); + LOG(INFO) << "Set pin " << kDefaultGpioIn << " direction to in"; + write_gpio(gpio_in, "direction", "in"); + LOG(INFO) << "Set pin " << kDefaultGpioIn << " edge to both"; + write_gpio(gpio_in, "edge", "both"); + + if (poll_gpio(gpio_in)) { + LOG(INFO) << "Pin " << kDefaultGpioIn << " pushed. Now turn off LED"; + write_gpio(gpio_out, "value", "0"); + } + return 0; +}
diff --git a/common/brillo_gpios/vendorsetup.sh b/common/brillo_gpios/vendorsetup.sh new file mode 100644 index 0000000..cdec5ff --- /dev/null +++ b/common/brillo_gpios/vendorsetup.sh
@@ -0,0 +1 @@ +add_lunch_combo brillo_gpios-userdebug
diff --git a/common/brillo_i2c/AndroidProducts.mk b/common/brillo_i2c/AndroidProducts.mk new file mode 100644 index 0000000..bb7c051 --- /dev/null +++ b/common/brillo_i2c/AndroidProducts.mk
@@ -0,0 +1,14 @@ +# 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. +PRODUCT_MAKEFILES := $(LOCAL_DIR)/brillo_i2c.mk
diff --git a/common/brillo_i2c/brillo_i2c.mk b/common/brillo_i2c/brillo_i2c.mk new file mode 100644 index 0000000..5ebc92a --- /dev/null +++ b/common/brillo_i2c/brillo_i2c.mk
@@ -0,0 +1,20 @@ +# 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. + +$(call inherit-product, device/generic/brillo/brillo_base.mk) + +PRODUCT_NAME := brillo_i2c +PRODUCT_BRAND := Brillo + +PRODUCT_DEVICE := dragonboard
diff --git a/common/brillo_i2c/src/i2c_service/Android.mk b/common/brillo_i2c/src/i2c_service/Android.mk new file mode 100644 index 0000000..4f4df94 --- /dev/null +++ b/common/brillo_i2c/src/i2c_service/Android.mk
@@ -0,0 +1,23 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := i2c_service +LOCAL_REQUIRED_MODULES := init.i2c_service.rc +LOCAL_SRC_FILES := i2c_service.cpp i2c_device.cpp +LOCAL_SHARED_LIBRARIES := libc libbase +LOCAL_CFLAGS := -Werror +include $(BUILD_EXECUTABLE)
diff --git a/common/brillo_i2c/src/i2c_service/i2c_device.cpp b/common/brillo_i2c/src/i2c_service/i2c_device.cpp new file mode 100644 index 0000000..35089b6 --- /dev/null +++ b/common/brillo_i2c/src/i2c_service/i2c_device.cpp
@@ -0,0 +1,131 @@ +// 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 "i2c_device.h" + +I2CDevice::I2CDevice(const char* device_filename, + const uint8_t device_address) : + // Does nothing except store the constant values. + address_(device_address), + filename_(device_filename) + + { + LOG(INFO) << "New i2c device for " << address_ << " at " << filename_; + } + + +int I2CDevice::Begin() { + // First, this opens the /dev device. + int rc = open_i2c(); + if (rc < 0) { + return rc; + } + // Then, registers it as a slave. + rc = enslave_i2c(); + if (rc < 0) { + return rc; + } + return 0; +} + + +int I2CDevice::End() { + return close(fd_); +} + + +int I2CDevice::open_i2c() { + // Opening the device is just a R/W file open. + int fd = open(filename_, O_RDWR); + if (-1 == fd) { + PLOG(ERROR) << "Could not open i2c device."; + return -1; + } + LOG(INFO) << "i2c device opened at " << filename_; + fd_ = fd; + return fd; +} + + +int I2CDevice::enslave_i2c() { + // The I2C_SLAVE ioctl is sent to the device at the given address. + int rc = ioctl(fd_, I2C_SLAVE, address_); + if (rc < 0) { + PLOG(ERROR) << "i2c device failed to become a slave."; + return rc; + } + printf("yes %x\n", address_); + LOG(INFO) << "i2c device is a slave at" << filename_; + return rc; +} + + +int I2CDevice::Send(struct i2c_msg messages[], + const size_t count) { + // Sending messages requires them to be wrapped in the i2c_rdwr_ioctl_data + // struct and sending them using the I2C_RDWR ioctl. + struct i2c_rdwr_ioctl_data data = { + .msgs = messages, + .nmsgs = count + }; + int rc = ioctl(fd_, I2C_RDWR, &data); + if (rc < 0) { + PLOG(ERROR) << "Unable to send data."; + } + return rc; +} + + +int I2CDevice::SetRegister(const uint8_t register_address, + const uint8_t value) { + // Setting a register involves sending a single message with a 2-byte payload. + // The first byte is the register address, and the second is the value. + uint8_t outbuf[] = {register_address, value}; + struct i2c_msg messages[1]; + messages[0] = { + .addr = address_, + .flags = 0, + .len = sizeof(outbuf), + .buf = outbuf + }; + return Send(messages, 1); +} + + +int I2CDevice::GetRegister(const uint8_t register_address, + uint8_t& value) { + // Reading a register involves sending two messages, each with a single-byte + // payload. The first message contains the register address and the second + // message is empty and will contain the response value. + struct i2c_msg messages[2]; + uint8_t outbuf = register_address; + // Both messages need the addr value set. + messages[0] = { + .addr = address_, + .flags = 0, + .len = 1, + .buf = &outbuf + }; + // Needs to pass the I2C_M_RD flag in order to get the value. + messages[1] = { + .addr = address_, + .flags = I2C_M_RD, + .len = 1, + .buf = &value, + }; + // Will contain the register value. + int rc = Send(messages, 2); + return rc; +}
diff --git a/common/brillo_i2c/src/i2c_service/i2c_device.h b/common/brillo_i2c/src/i2c_service/i2c_device.h new file mode 100644 index 0000000..62eec04 --- /dev/null +++ b/common/brillo_i2c/src/i2c_service/i2c_device.h
@@ -0,0 +1,70 @@ +// 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. + +// Defines a single i2c device at an address for a named i2c bus. +// +// Can be used to read and write i2c registers, or send arbitrary streams of +// messages. + + +#include <fcntl.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <android-base/logging.h> + + +class I2CDevice { + + public: + // Creates an I2CDevice for a given filename and address. + // + // For example: I2CDevice my_device("/dev/i2c-0", 0x20); + // + // Note: this constructor does not actually open the device, Begin() must be + // called at the appropriate time. + I2CDevice(const char* device_filename, + const uint8_t device_address); + // Opens the device for reading and writing. Returns 0 on success, or the + // error code from the open/ioctl operations. + int Begin(); + // Closes the device. Returns 0 on success or the error code from the close + // operation. + int End(); + // Sends a sequence of I2C messages. This is used for write operations AND + // read operations, since reads are just an empty outbound message. Returns 0 + // on success or the error code from the ioctl operation. + int Send(struct i2c_msg messages[], + const size_t count); + // Sets a register at a given address to a given value. Returns 0 on success + // or the error code from the ioctl operation. + int SetRegister(const uint8_t register_address, + const uint8_t value); + // Reads a register into a passed value. Returns 0 on success or the error + // code from the ioctl operation. + int GetRegister(const uint8_t register_address, + uint8_t& value); + + private: + // Demonstrates opening a device. + int open_i2c(); + // Demonstrates sending the I2C_SLAVE ioctl. + int enslave_i2c(); + + // Stores the device address. + const uint8_t address_; + // Stores the device filename. + const char* filename_; + // Stores the open file descriptor for the device. + int fd_; +};
diff --git a/common/brillo_i2c/src/i2c_service/i2c_service.cpp b/common/brillo_i2c/src/i2c_service/i2c_service.cpp new file mode 100644 index 0000000..dd6f9c8 --- /dev/null +++ b/common/brillo_i2c/src/i2c_service/i2c_service.cpp
@@ -0,0 +1,121 @@ +// 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. +// +// +// Example of using i2c devices with Brillo. +// +// These use /dev/i2c-6. On the dragonboard 410c, this device maps to i2c1 on +// pins 19 (SCL) and 21 (SDA). + + +#include <android-base/logging.h> + +#include "i2c_device.h" + + +// Example of using the mcp23017 IO port expander. +// +// On the expander, pin0 is an output pin, and pin1 is an input pin, simply +// connected to gnd through an led or switch. +// +// 0 -> LED -> gnd +// 1 -> SWITCH -> gnd +// +// i2c is connected directly to the board. Pin1 will use the internal pullup +// resistor of the mcp23017. +// +void io_expander_demo() { + // Opens the device at i2c1 at the default address (the default address is + // calculated when A0 A1 A2 pins are all pulled low). + I2CDevice mcp23017("/dev/i2c-6", 0x20); + mcp23017.Begin(); + // Sets the pin1 as an input, the rest as output. + mcp23017.SetRegister(0x0, 0b00000010); + // Sets the internal pullup resistor for pin1. + mcp23017.SetRegister(0xC, 0b00000010); + // Sets the initial values of the GPIOA register. + mcp23017.SetRegister(0x12, 0b00000010); + uint8_t register_value; + while(1) { + // Reads the GPIOA register. + mcp23017.GetRegister(0x12, register_value); + // Decides if switch is on based on whether pin1 is low. + uint8_t is_switch_on = (register_value & 0b00000010) >> 1; + // Sets the GPIO register at pin0 to reflect the switch state on pin1. + mcp23017.SetRegister(0x12, is_switch_on ^ 0b10000001); + LOG(INFO) << (is_switch_on ? "On." : "Off."); + // Waiting a bit is sensible. + usleep(0x10000); + } +} + + +// Example of using the ds1307 real time clock. +// +// i2c is connected directly to the board. +// +void rtc_demo() { + // Opens the device at i2c1 at the default address (the default address is + // calculated when A0 A1 A2 pins are all pulled low). + I2CDevice ds1307("/dev/i2c-6", 0x68); + ds1307.Begin(); + // Needs three bytes, one for each of seconds, minutes, hours. + uint8_t seconds_register_value = 0x0; + uint8_t minutes_register_value = 0x0; + uint8_t hours_register_value = 0x0; + char time_string[10]; + while (1) { + ds1307.GetRegister(0x0, seconds_register_value); + ds1307.GetRegister(0x1, minutes_register_value); + ds1307.GetRegister(0x2, hours_register_value); + // Munges the values based on their storage scheme. + int seconds = 10 * ((seconds_register_value & 0b01110000) >> 4) + + (seconds_register_value & 0b00001111); + int minutes = 10 * ((minutes_register_value & 0b01110000) >> 4) + + (minutes_register_value & 0b00001111); + int hours = 10 * ((hours_register_value & 0b00110000) >> 4) + + (hours_register_value & 0b00001111); + sprintf(time_string, "%02d:%02d:%02d", hours, minutes, seconds); + LOG(INFO) << "Time is:" << time_string; + sleep(1); + } +} + + +// Example of reading the ds1307 real time clock, and writing to the mcp23017 +// port expander. +// +// This demo uses both devices at the same time. +void binary_watch_demo() { + I2CDevice mcp23017("/dev/i2c-6", 0x20); + mcp23017.Begin(); + mcp23017.SetRegister(0x0, 0b00000000); + mcp23017.SetRegister(0x12, 0b00000000); + I2CDevice ds1307("/dev/i2c-6", 0x68); + ds1307.Begin(); + uint8_t seconds_register_value = 0x0; + while(1) { + ds1307.GetRegister(0x0, seconds_register_value); + mcp23017.SetRegister(0x12, seconds_register_value << 2); + sleep(1); + } +} + + +int main(int argc __unused, char **argv __unused) { + //io_expander_demo(); + //rtc_demo(); + binary_watch_demo(); + return 0; +}
diff --git a/common/brillo_i2c/vendorsetup.sh b/common/brillo_i2c/vendorsetup.sh new file mode 100644 index 0000000..907895a --- /dev/null +++ b/common/brillo_i2c/vendorsetup.sh
@@ -0,0 +1,14 @@ +# 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. +add_lunch_combo brillo_i2c-userdebug
diff --git a/common/brillo_leds/AndroidProducts.mk b/common/brillo_leds/AndroidProducts.mk new file mode 100644 index 0000000..7f9fdb1 --- /dev/null +++ b/common/brillo_leds/AndroidProducts.mk
@@ -0,0 +1 @@ +PRODUCT_MAKEFILES := $(LOCAL_DIR)/brillo_leds.mk
diff --git a/common/brillo_leds/README b/common/brillo_leds/README new file mode 100644 index 0000000..e0fe8ec --- /dev/null +++ b/common/brillo_leds/README
@@ -0,0 +1,23 @@ +README for Brillo LED sample + +This LED sample creates a service that can be run on Brillo Dragonboard. It animates LED 1 through LED 4. + +Once built, you can find the binary inside your AOSP tree at: + +out/target/product/dragonboard/symbols/system/bin/led_service + +Do the following to deploy the sample: + + - adb root + - adb remount + - adb sync + +Do the following to run the sample on Dragonboard: + + - adb shell + - /system/bin/led_service & + - logcat + +Note the last step shows logging info + +
diff --git a/common/brillo_leds/brillo_leds.mk b/common/brillo_leds/brillo_leds.mk new file mode 100644 index 0000000..cc2b31e --- /dev/null +++ b/common/brillo_leds/brillo_leds.mk
@@ -0,0 +1,8 @@ +$(call inherit-product, device/generic/brillo/brillo_base.mk) + +PRODUCT_NAME := brillo_leds +PRODUCT_BRAND := Brillo + +PRODUCT_DEVICE := dragonboard +PRODUCT_PACKAGES += brillo_led_tool +
diff --git a/common/brillo_leds/src/brillo_led_tool/Android.mk b/common/brillo_leds/src/brillo_led_tool/Android.mk new file mode 100644 index 0000000..b9a1320 --- /dev/null +++ b/common/brillo_leds/src/brillo_led_tool/Android.mk
@@ -0,0 +1,7 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := brillo_led_tool +LOCAL_SRC_FILES := brillo_led_tool.cpp +LOCAL_SHARED_LIBRARIES := libc libbase +LOCAL_CFLAGS := -Werror +include $(BUILD_EXECUTABLE)
diff --git a/common/brillo_leds/src/brillo_led_tool/brillo_led_tool.cpp b/common/brillo_leds/src/brillo_led_tool/brillo_led_tool.cpp new file mode 100644 index 0000000..10454f7 --- /dev/null +++ b/common/brillo_leds/src/brillo_led_tool/brillo_led_tool.cpp
@@ -0,0 +1,33 @@ +#include <unistd.h> +#include <android-base/logging.h> + +int main(int argc __unused, char **argv __unused) { + static const char* const leds[] = { + "/sys/class/leds/led1/brightness", + "/sys/class/leds/led2/brightness", + "/sys/class/leds/led3/brightness", + "/sys/class/leds/boot/brightness" + }; + + LOG(INFO) << "starting blinking LEDs"; + int i = 0; + for(;;) { + FILE *fp; + fp = fopen(leds[i], "w"); + if (fp == NULL) { + PLOG(ERROR) << "Error opening file"; + return 0; + } else { + fputs("255", fp); + fseek(fp, 0, SEEK_SET); + sleep(1); + fputs("0", fp); + fclose(fp); + LOG(INFO) << "toggling LED " << i + 1 << " on/off"; + i++; + i = i % 4; + } + } + LOG(INFO) << "exiting"; + return 0; +}
diff --git a/common/brillo_leds/vendorsetup.sh b/common/brillo_leds/vendorsetup.sh new file mode 100644 index 0000000..ace4f2e --- /dev/null +++ b/common/brillo_leds/vendorsetup.sh
@@ -0,0 +1 @@ +add_lunch_combo brillo_leds-userdebug
diff --git a/common/dbus_example/Android.mk b/common/dbus_example/Android.mk new file mode 100644 index 0000000..5b97f08 --- /dev/null +++ b/common/dbus_example/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) + +dbus_exampleCommonCppExtension := .cc +dbus_exampleCommonCFlags := -Wall -Werror -Wno-sign-promo \ + -Wno-unused-parameter +dbus_exampleCommonSharedLibraries := \ + libchrome \ + libchrome-dbus \ + libdbus \ + liblog \ + +include $(CLEAR_VARS) +LOCAL_MODULE := dbus-example-daemon +LOCAL_SRC_FILES := dbus-example-daemon.cc +LOCAL_SHARED_LIBRARIES := $(dbus_exampleCommonSharedLibraries) +LOCAL_CPP_EXTENSION := $(dbus_exampleCommonCppExtension) +LOCAL_CFLAGS := $(dbus_exampleCommonCFlags) +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := dbus-example-client +LOCAL_SRC_FILES := dbus-example-client.cc +LOCAL_SHARED_LIBRARIES := $(dbus_exampleCommonSharedLibraries) +LOCAL_CPP_EXTENSION := $(dbus_exampleCommonCppExtension) +LOCAL_CFLAGS := $(dbus_exampleCommonCFlags) +include $(BUILD_EXECUTABLE)
diff --git a/common/dbus_example/build.gradle b/common/dbus_example/build.gradle new file mode 100644 index 0000000..4271050 --- /dev/null +++ b/common/dbus_example/build.gradle
@@ -0,0 +1,52 @@ +/* + * 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. + */ + +/* Return the android build root. */ +def gettop = { + File cwd = new File('.').absoluteFile + while (!(new File(cwd, 'build/core/envsetup.mk')).isFile()) { + cwd = cwd.parentFile + } + return cwd +} + +ext.android_build_root = gettop() +ext.nr_threads = Runtime.runtime.availableProcessors() + +/* Invoke an Android Build System command. */ +def call_abs(cmd) { + String full_cmd = 'source "' + android_build_root + '/build/envsetup.sh" && ' + cmd + println 'Calling ' + full_cmd + exec { + commandLine 'sh', '-c', full_cmd + } +} + +task build << { + call_abs 'mm -j ' + nr_threads +} + +task clean << { + call_abs 'm clean' +} + +task buildAll << { + call_abs 'm -j ' + nr_threads +} + +task emulator(type: Exec) { + commandLine 'emulator-arm' +}
diff --git a/common/dbus_example/constants.h b/common/dbus_example/constants.h new file mode 100644 index 0000000..2286440 --- /dev/null +++ b/common/dbus_example/constants.h
@@ -0,0 +1,30 @@ +/* + * 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 DEVICE_GENERIC_BRILLO_EXAMPLES_DBUS_CONSTANTS_H_ +#define DEVICE_GENERIC_BRILLO_EXAMPLES_DBUS_CONSTANTS_H_ + +namespace dbus_example { + +// D-Bus-related constants. +static const char kServicePath[] = "/com/android/brillo/DBusExample"; +static const char kServiceName[] = "com.android.brillo.DBusExample"; +static const char kInterface[] = "com.android.brillo.DBusExample"; +static const char kMethodName[] = "Ping"; + +} // namespace dbus_example + +#endif // DEVICE_GENERIC_BRILLO_EXAMPLES_DBUS_CONSTANTS_H_
diff --git a/common/dbus_example/dbus-example-client.cc b/common/dbus_example/dbus-example-client.cc new file mode 100644 index 0000000..171bc6f --- /dev/null +++ b/common/dbus_example/dbus-example-client.cc
@@ -0,0 +1,68 @@ +/* + * 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 <unistd.h> + +#include <memory> + +#include <dbus/bus.h> +#include <dbus/message.h> +#include <dbus/object_proxy.h> +#include <utils/Log.h> + +#include "constants.h" + +// Used implicitly. +#undef LOG_TAG +#define LOG_TAG "dbus-example-client" + +int main(int argc, char *argv[]) { + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + scoped_refptr<dbus::Bus> bus(new dbus::Bus(options)); + CHECK(bus->Connect()); + dbus::ObjectProxy* proxy = bus->GetObjectProxy( + dbus_example::kServiceName, dbus::ObjectPath(dbus_example::kServicePath)); + CHECK(proxy); + + int32_t token = 1; + while (true) { + dbus::MethodCall method_call(dbus_example::kInterface, + dbus_example::kMethodName); + dbus::MessageWriter writer(&method_call); + writer.AppendInt32(token); + + ALOGI("Calling %s with %d", dbus_example::kMethodName, token); + std::unique_ptr<dbus::Response> response( + proxy->CallMethodAndBlock( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (!response) { + ALOGE("Didn't receive response from server"); + } else { + int32_t response_token = 0; + dbus::MessageReader reader(response.get()); + if (!reader.PopInt32(&response_token)) + ALOGE("Missing token in response from server"); + else + ALOGI("Received %d", response_token); + } + + token++; + sleep(1); + } + + return 0; +}
diff --git a/common/dbus_example/dbus-example-daemon.cc b/common/dbus_example/dbus-example-daemon.cc new file mode 100644 index 0000000..6a069e1 --- /dev/null +++ b/common/dbus_example/dbus-example-daemon.cc
@@ -0,0 +1,98 @@ +/* + * 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 <memory> + +#include <base/at_exit.h> +#include <base/bind.h> +#include <base/memory/ref_counted.h> +#include <base/message_loop/message_loop.h> +#include <base/run_loop.h> +#include <dbus/bus.h> +#include <dbus/exported_object.h> +#include <dbus/message.h> +#include <utils/Log.h> + +#include "constants.h" + +// Used implicitly. +#undef LOG_TAG +#define LOG_TAG "dbus-example-daemon" + +namespace dbus_example { +namespace { + +class Daemon { + public: + Daemon() : dbus_object_(nullptr) {} + ~Daemon() {} + + void Init() { + ALOGI("Connecting to system bus"); + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + bus_ = new dbus::Bus(options); + CHECK(bus_->Connect()); + + ALOGI("Exporting method %s on %s", kMethodName, kServicePath); + dbus_object_ = bus_->GetExportedObject(dbus::ObjectPath(kServicePath)); + CHECK(dbus_object_->ExportMethodAndBlock( + kInterface, kMethodName, + base::Bind(&Daemon::HandleMethodCall, base::Unretained(this)))); + + ALOGI("Requesting ownership of %s", kServiceName); + CHECK(bus_->RequestOwnershipAndBlock(kServiceName, + dbus::Bus::REQUIRE_PRIMARY)); + } + + private: + void HandleMethodCall(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + int32_t token = 0; + dbus::MessageReader reader(method_call); + if (!reader.PopInt32(&token)) { + ALOGE("Request didn't have token"); + response_sender.Run(std::unique_ptr<dbus::Response>( + dbus::ErrorResponse::FromMethodCall(method_call, + DBUS_ERROR_INVALID_ARGS, "Expected token argument"))); + return; + } + + std::unique_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + writer.AppendInt32(token); + ALOGI("Replying to request with token %d", token); + response_sender.Run(std::move(response)); + } + + scoped_refptr<dbus::Bus> bus_; + dbus::ExportedObject* dbus_object_; // weak; owned by |bus_| + + DISALLOW_COPY_AND_ASSIGN(Daemon); +}; + +} // namespace +} // namespace dbus_example + +int main(int argc, char *argv[]) { + base::AtExitManager at_exit; + base::MessageLoopForIO loop; + dbus_example::Daemon daemon; + daemon.Init(); + base::RunLoop().Run(); + return 0; +}
diff --git a/common/keyboard_example/Android.mk b/common/keyboard_example/Android.mk new file mode 100644 index 0000000..f2c5942 --- /dev/null +++ b/common/keyboard_example/Android.mk
@@ -0,0 +1,24 @@ +# +# 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 := keyboard-example +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := keyboard_example.cpp +include $(BUILD_EXECUTABLE)
diff --git a/common/keyboard_example/README b/common/keyboard_example/README new file mode 100644 index 0000000..7ecd119 --- /dev/null +++ b/common/keyboard_example/README
@@ -0,0 +1,14 @@ +This folder contains example code for writing native apps that use keyboard +inputs. + +Example file: + keyboard_example.cpp: + Demos listing devices and querying their metadata, polling for keyboard + events and detecting different events (pressed, released, hold) for a given + key. + + This uses the evdev kernel mechanism to handle input events. It does not use + the input HAL. + See https://www.kernel.org/doc/Documentation/input/input.txt and + https://www.kernel.org/doc/Documentation/input/event-codes.txt for more + information.
diff --git a/common/keyboard_example/keyboard_example.cpp b/common/keyboard_example/keyboard_example.cpp new file mode 100644 index 0000000..3358be9 --- /dev/null +++ b/common/keyboard_example/keyboard_example.cpp
@@ -0,0 +1,185 @@ +/* + * 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 <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/input.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/poll.h> +#include <unistd.h> + +#include <string> +#include <vector> + +using std::string; +using std::vector; + +static const int kMaxDeviceName = 80; + +// Reads the list of files in |directory_path| into |filenames|. +void ListDirectory(const string& directory_path, vector<string>* filenames) { + if (!filenames) { + printf("Error: filenames is null.\n"); + exit(1); + } + DIR* directory = opendir(directory_path.c_str()); + if (!directory) { + printf("Failed to open %s.\n", directory_path.c_str()); + return; + } + struct dirent *entry = NULL; + while ((entry = readdir(directory))) { + if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { + filenames->push_back(directory_path + "/" + entry->d_name); + } + } +} + +// Returns true iff the device reports EV_KEY events. +bool HasKeyEvents(int device_fd) { + unsigned long evbit = 0; + // Get the bit field of available event types. + ioctl(device_fd, EVIOCGBIT(0, sizeof(evbit)), &evbit); + return evbit & (1 << EV_KEY); +} + +// Returns true iff the given device has |key|. +bool HasSpecificKey(int device_fd, unsigned int key) { + size_t nchar = KEY_MAX/8 + 1; + unsigned char bits[nchar]; + // Get the bit fields of available keys. + ioctl(device_fd, EVIOCGBIT(EV_KEY, sizeof(bits)), &bits); + return bits[key/8] & (1 << (key % 8)); +} + +int main(int argc, char** argv) { + string input_directory = "/dev/input"; + if (argc < 2) { + printf("No input directory specified, using the default one: %s.\n", + input_directory.c_str()); + } else { + input_directory = argv[1]; + } + + vector<string> filenames; + ListDirectory(input_directory, &filenames); + printf("%i devices found in %s.\n", filenames.size(), + input_directory.c_str()); + + // Initializing the pollfd structures. + vector<pollfd> poll_fds; + char device_name[kMaxDeviceName]; + for (size_t i = 0; i < filenames.size(); i++) { + int fd = open(filenames[i].c_str(), O_RDONLY); + if (fd < 0) { + printf("Failed to open %s for reading: %s\n", filenames[i].c_str(), + strerror(errno)); + return 1; + } + + // Reads the device name. + if(ioctl(fd, EVIOCGNAME(sizeof(device_name) - 1), + &device_name) < 1) { + device_name[0] = '\0'; + } + + if (!HasKeyEvents(fd)) { + printf("Discarding %s (%s): does not report any EV_KEY events.\n", + filenames[i].c_str(), device_name); + continue; + } + if (!HasSpecificKey(fd, KEY_B)) { + printf("Discarding %s (%s): does not have a B key.\n", + filenames[i].c_str(), device_name); + continue; + } + poll_fds.push_back(pollfd{fd, POLLIN, 0}); + + printf("Adding device %s: %s\n", filenames[i].c_str(), device_name); + } + + if (poll_fds.empty()) { + printf("No keyboard detected, exiting.\n"); + return 1; + } + + // Number of repeated events + 1. + int count = 1; + // Events accumulated for each device. + vector<vector<input_event>> events(poll_fds.size()); + + const char instructions[] = "Press B to start the game and release B to go " + "back to the main menu."; + printf("%s\n", instructions); + while (true) { + // Wait for data to be available on one of the file descriptors without + // timeout (-1). + poll(poll_fds.data(), poll_fds.size(), -1); + + for (size_t i = 0; i < poll_fds.size(); i++) { + if (poll_fds[i].revents & POLLIN) { + struct input_event event; + if (read(poll_fds[i].fd, &event, sizeof(event)) != sizeof(event)) { + printf("Failed to read an event.\n"); + return 1; + } + + // When receiving a key event for B, add it to the list of events. + // Don't process it yet as there might be other events in that report. + if (event.type == EV_KEY && event.code == KEY_B) { + events[i].push_back(event); + } + + // A SYN_REPORT event signals the end of a report, process all the + // previously accumulated events. + // At that point we only have events for B in the event vector. + if (event.type == EV_SYN && event.code == SYN_REPORT) { + for (vector<input_event>::iterator it = events[i].begin(); + it != events[i].end(); it++) { + switch (it->value) { + case 0: { + // A value of 0 indicates a "key released" event. + double percent = 1. - 1./count; + printf("%.02f%% loaded.\n", percent * 100); + printf("B released, returning to the main menu.\n\n"); + printf("%s\n", instructions); + break; + } + case 1: { + // A value of 1 indicates a "key pressed" event. + printf("B pressed, starting the game...\n"); + count = 1; + break; + } + case 2: { + // A value of 2 indicates a "key repeat" event. + count++; + break; + } + default: {} + } + } + // Discard all processed events. + events[i].clear(); + } + } + } + } + return 0; +} +
diff --git a/common/lights_example/example-app/Android.mk b/common/lights_example/example-app/Android.mk new file mode 100644 index 0000000..6e3caae --- /dev/null +++ b/common/lights_example/example-app/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 := lights-hal-example-app +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := hal-example-app.cpp +LOCAL_CFLAGS := -Wall -Werror +LOCAL_SHARED_LIBRARIES := \ + libc \ + libhardware \ + +include $(BUILD_EXECUTABLE)
diff --git a/common/lights_example/example-app/README b/common/lights_example/example-app/README new file mode 100644 index 0000000..a8fc128 --- /dev/null +++ b/common/lights_example/example-app/README
@@ -0,0 +1,8 @@ +This folder contains example code for writing native apps that use Brillo LED +lights. + +Example files: + hal-example-app.cpp: + An example app that uses the lights HAL (defined in + hardware/libhardware/include/lights.h) directly. The app turns on the LED + light used for notifications, and turns it off 3 seconds later.
diff --git a/common/lights_example/example-app/hal-example-app.cpp b/common/lights_example/example-app/hal-example-app.cpp new file mode 100644 index 0000000..f014bd0 --- /dev/null +++ b/common/lights_example/example-app/hal-example-app.cpp
@@ -0,0 +1,69 @@ +/* + * 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 <err.h> +#include <stdio.h> +#include <unistd.h> + +#include <hardware/hardware.h> +#include <hardware/lights.h> + +int main() { + const hw_module_t* module = nullptr; + struct light_device_t* light_device = nullptr; + + int ret = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &module); + if (ret || !module) { + err(1, "Failed to load %s module", LIGHTS_HARDWARE_MODULE_ID); + } + + ret = module->methods->open( + module, LIGHT_ID_NOTIFICATIONS, + reinterpret_cast<struct hw_device_t**>(&light_device)); + if (ret || !light_device) { + err(1, "Failed to open light device for %s", LIGHT_ID_NOTIFICATIONS); + } + + struct light_state_t state = { + .color = 0, + .flashMode = LIGHT_FLASH_NONE, + .flashOnMS = 0, + .flashOffMS = 0, + .brightnessMode = 0, + }; + + // Turn light on for three seconds. + state.color = 1; + light_device->set_light(light_device, &state); + sleep(3); + + // Flash for three seconds. + state.flashMode = LIGHT_FLASH_TIMED; + state.flashOnMS = 50; + state.flashOffMS = 50; + light_device->set_light(light_device, &state); + sleep(3); + + // Turn light off. + state.color = 0; + state.flashMode = LIGHT_FLASH_NONE; + light_device->set_light(light_device, &state); + + light_device->common.close( + reinterpret_cast<struct hw_device_t*>(light_device)); + + return 0; +}
diff --git a/common/sensors_example/Android.mk b/common/sensors_example/Android.mk new file mode 100644 index 0000000..e423f15 --- /dev/null +++ b/common/sensors_example/Android.mk
@@ -0,0 +1,44 @@ +# +# 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) + +# Example Sensors HAL implementation. +include $(CLEAR_VARS) +LOCAL_MODULE := sensors.example +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -Wno-unused-parameter +LOCAL_SRC_FILES := \ + example_sensors.cpp \ + sensors_hal.cpp +include $(BUILD_SHARED_LIBRARY) + +# Example app that uses sensors HAL. +include $(CLEAR_VARS) +LOCAL_MODULE := sensors-hal-example-app +LOCAL_SRC_FILES := hal-example-app.cpp +LOCAL_CFLAGS := -Wno-unused-parameter +LOCAL_SHARED_LIBRARIES := libhardware +include $(BUILD_EXECUTABLE) + +# Example app that uses NDK sensors API. +include $(CLEAR_VARS) +LOCAL_MODULE := sensors-ndk-example-app +LOCAL_SRC_FILES := ndk-example-app.cpp +LOCAL_CFLAGS := -Wno-unused-parameter +LOCAL_SHARED_LIBRARIES := libsensor +include $(BUILD_EXECUTABLE)
diff --git a/common/sensors_example/README b/common/sensors_example/README new file mode 100644 index 0000000..a8d2c61 --- /dev/null +++ b/common/sensors_example/README
@@ -0,0 +1,26 @@ +This folder contains example code for writing native apps that use Brillo +sensors. The sensors HAL implementation must exist on the device for the apps +in this folder to run successfully. + +Example files: + hal-example-app.cpp: + An example app that uses the sensors HAL (defined in + hardware/libhardware/include/sensors.h) directly. In the app, the list of + sensors found on the device is printed. And if an accelerometer exists, the + app will read the data and print it out. + + ndk-example-app.cpp: + An example app that uses the NDK sensors interface. In the app, the list of + sensors found on the device is printed. And then the app tries to read data + from accelerometer, proximity sensor, and significant motion sensor, which + has continuous, on-change, and one-shot reporting modes respectively. + + sensors_hal.cpp + An example implementation of a sensors HAL. It implements a fake + accelerometer and custom sensor (defined in example_sensors.{h|cpp}). The + accelerometer always returns random data. The custom sensor returns the hour + for the local time. + + To use this HAL implementation, push the binary file sensors.example into + /system/lib/hw/ folder and rename it to sensors.default (make sure there is + no other sensors.* file under that folder).
diff --git a/common/sensors_example/example_sensors.cpp b/common/sensors_example/example_sensors.cpp new file mode 100644 index 0000000..22d5381 --- /dev/null +++ b/common/sensors_example/example_sensors.cpp
@@ -0,0 +1,76 @@ +/* + * 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 "example_sensors.h" + +#include <stdlib.h> +#include <time.h> + +#include <hardware/sensors.h> + +SensorBase::SensorBase() {} +SensorBase::~SensorBase() {} + +int SensorBase::activate(int handle, int enabled) { + return 0; +} + +int SensorBase::setDelay(int handle, int64_t ns) { + return 0; +} + +int SensorBase::batch(int handle, int flags, + int64_t period_ns, int64_t timeout) { + return 0; +} + +int SensorBase::flush(int handle) { + return 0; +} + + +Accelerometer::Accelerometer() {} +Accelerometer::~Accelerometer() {} + +int Accelerometer::pollEvents(sensors_event_t* data, int count) { + // Returns fake random values. + data->acceleration.x = static_cast<float>(random() % kMaxRange); + data->acceleration.y = static_cast<float>(random() % kMaxRange); + data->acceleration.z = static_cast<float>(random() % kMaxRange); + return 1; +} + + +CustomSensor::CustomSensor() {} +CustomSensor::~CustomSensor() {} + +int CustomSensor::pollEvents(sensors_event_t* data, int count) { + if (data) { + // For this custom sensor, we will return the hour of the local time. + time_t t = time(NULL); + struct tm* now = localtime(&t); + if (now) { + // Any field can be used to return data for a custom sensor. + // See definition of struct sensors_event_t in hardware/sensors.h for the + // available fields. + data->data[0] = now->tm_hour; + return 1; + } + } + // Note: poll() return value is the number of events being returned. We use + // -1 to signal an error. + return -1; +}
diff --git a/common/sensors_example/example_sensors.h b/common/sensors_example/example_sensors.h new file mode 100644 index 0000000..5e984ea --- /dev/null +++ b/common/sensors_example/example_sensors.h
@@ -0,0 +1,54 @@ +/* + * 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 SENSORS_EXAMPLE_SENSORS_H +#define SENSORS_EXAMPLE_SENSORS_H + +#include <stdint.h> + +struct sensors_event_t; + +class SensorBase { + public: + SensorBase(); + virtual ~SensorBase(); + + virtual int activate(int handle, int enabled); + virtual int setDelay(int handle, int64_t ns); + virtual int pollEvents(sensors_event_t* data, int count) = 0; + virtual int batch(int handle, int flags, int64_t period_ns, int64_t timeout); + virtual int flush(int handle); +}; + +class Accelerometer : public SensorBase { + public: + static const int kMaxRange = 1000; + + Accelerometer(); + ~Accelerometer() override; + + int pollEvents(sensors_event_t* data, int count) override; +}; + +class CustomSensor : public SensorBase { + public: + CustomSensor(); + ~CustomSensor() override; + + int pollEvents(sensors_event_t* data, int count) override; +}; + +#endif // SENSORS_EXAMPLE_SENSORS_H
diff --git a/common/sensors_example/hal-example-app.cpp b/common/sensors_example/hal-example-app.cpp new file mode 100644 index 0000000..dfec435 --- /dev/null +++ b/common/sensors_example/hal-example-app.cpp
@@ -0,0 +1,239 @@ +/* + * 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. + */ + +/* This file contains an example app that uses sensors HAL. + * It accepts a sensor type as an input argument and reads data from a + * sensor of that type. + * + * For any specified sensor type, there may be multiple such sensors defined + * in the HAL. Some or all of the defined sensors will actually be equipped. + * This test program will scan the HAL for the first equipped sensor of + * the specified sensor type and dump data from that sensor. + */ + +#include <getopt.h> +#include <math.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <hardware/hardware.h> +#include <hardware/sensors.h> + +#include <sys/types.h> + +#define DEFAULT_SENSOR_TYPE SENSOR_TYPE_ACCELEROMETER + +// Structure to hold the decoded command line options +struct pgm_options { + int sensor_type; +}; + +// Be sure to keep the options for longopts and shortopts in the same order +// so that Usage() is correct. +static struct option longopts[] = { + {"help", no_argument, NULL, '?'}, + {"accel", no_argument, NULL, 'a'}, + {"temp", no_argument, NULL, 't'}, + {"light", no_argument, NULL, 'l'}, + {"orient", no_argument, NULL, 'o'}, + {"prox", no_argument, NULL, 'p'}, + {"motion", no_argument, NULL, 'm'}, + {NULL, 0, NULL, 0} +}; +static char shortopts[] = "?atlopm"; + +// Describes the options for this program. +void Usage(char *pgm_name) { + printf("Usage: %s [options...]\n", pgm_name); + printf("Exercises the sensors HAL by direct calling.\n"); + printf("Options:\n"); + for (int i = 0; longopts[i].name; i++) { + printf(" --%-6s or -%c\n", longopts[i].name, shortopts[i]); + } +} + +// Processes all command line options. +// sets the options members for commnd line options +// returns (0) on success, -1 otherwise. +int ReadOpts(int argc, char **argv, struct pgm_options *options) { + int ch = 0; + + if (!options) { + fprintf(stderr, "Invalid options pointer\n"); + return 1; + } + + while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (ch) { + case 'a': + options->sensor_type = SENSOR_TYPE_ACCELEROMETER; + break; + case 't': + options->sensor_type = SENSOR_TYPE_TEMPERATURE; + break; + case 'l': + options->sensor_type = SENSOR_TYPE_LIGHT; + break; + case 'o': + options->sensor_type = SENSOR_TYPE_ORIENTATION; + break; + case 'p': + options->sensor_type = SENSOR_TYPE_PROXIMITY; + break; + case 'm': + options->sensor_type = SENSOR_TYPE_SIGNIFICANT_MOTION; + break; + default: + Usage(argv[0]); + return -1; + } + } + argc -= optind; + argv += optind; + return 0; +} + +// Prints data associated with each supported sensor type. +// Be sure to provide a case for each sensor type supported in +// the ReadOpts() function. +void DisplaySensorData(int sensor_type, const sensors_event_t *data) { + switch (sensor_type) { + case SENSOR_TYPE_PROXIMITY: + printf("Proximity distance: %f\n", data->distance); + break; + case SENSOR_TYPE_SIGNIFICANT_MOTION: + if (data->data[0] == 1) { + printf("Significant motion detected\n"); + } + break; + case SENSOR_TYPE_ACCELEROMETER: + printf("Acceleration: x = %f, y = %f, z = %f\n", + data->acceleration.x, data->acceleration.y, data->acceleration.z); + break; + case SENSOR_TYPE_TEMPERATURE: + printf("Temperature: %f\n", data->temperature); + break; + case SENSOR_TYPE_LIGHT: + printf("Light: %f\n", data->light); + break; + case SENSOR_TYPE_ORIENTATION: { + float heading = + atan2(static_cast<double>(data->orientation.y), + static_cast<double>(data->orientation.x)) * 180.0 / M_PI; + if (heading < 0.0) + heading += 360.0; + printf("Heading: %f, Orientation: x = %f, y = %f, z = %f\n", + heading, data->orientation.x, data->orientation.y, + data->orientation.z); + } + break; + } +} + +int main(int argc, char* argv[]) { + pgm_options options = {DEFAULT_SENSOR_TYPE}; + if (ReadOpts(argc, argv, &options) < 0) + return 1; + + sensors_module_t* sensor_module = nullptr; + int ret = hw_get_module(SENSORS_HARDWARE_MODULE_ID, + const_cast<hw_module_t const**>( + reinterpret_cast<hw_module_t**>(&sensor_module))); + if (ret || !sensor_module) { + fprintf(stderr, "Failed to load %s module: %s\n", + SENSORS_HARDWARE_MODULE_ID, strerror(-ret)); + return 1; + } + + const sensor_t *sensor_list = nullptr; + int sensor_count = + sensor_module->get_sensors_list(sensor_module, &sensor_list); + printf("Found %d supported sensors\n", sensor_count); + for (int i = 0; i < sensor_count; i++) { + printf("HAL supports sensor %s\n", sensor_list[i].name); + } + + // sensors_open_1 is used in HAL versions >= 1.0. + sensors_poll_device_1_t* sensor_device = nullptr; + ret = sensors_open_1(&sensor_module->common, &sensor_device); + if (ret || !sensor_device) { + fprintf(stderr, "Failed to open the sensor HAL\n"); + return 1; + } + + // Find the first sensor of the specified type that can be opened + bool sensor_found = false; + const sensor_t *sensor = nullptr; + for (int i = 0; i < sensor_count; i++) { + sensor = &sensor_list[i]; + if (sensor->type != options.sensor_type) + continue; + if (sensor_device->activate( + reinterpret_cast<sensors_poll_device_t*>(sensor_device), + sensor->handle, 1 /* enabled */) < 0) { + continue; + } + + // Found an equipped sensor of the specified type. + sensor_found = true; + break; + } + + if (!sensor_found) { + fprintf(stderr, "No sensor of the specified type found\n"); + ret = sensors_close_1(sensor_device); + if (ret) + fprintf(stderr, "Failed to close the sensor device\n"); + return 1; + } + printf("\nSensor %s activated\n", sensor->name); + + const int kNumEvents = 1; + const int kNumSamples = 10; + const int kWaitTimeSecs = 1; + for (int i = 0; i < kNumSamples; i++) { + sensors_event_t data[kNumEvents]; + memset(data, 0, sizeof(data)); + int event_count = sensor_device->poll( + reinterpret_cast<sensors_poll_device_t*>(sensor_device), + data, kNumEvents); + if (!event_count) { + fprintf(stderr, "Failed to read data from the sensor.\n"); + continue; + } + + DisplaySensorData(options.sensor_type, data); + sleep(kWaitTimeSecs); + } + + ret = sensor_device->activate( + reinterpret_cast<sensors_poll_device_t*>(sensor_device), + sensor->handle, 0 /* disabled */); + if (ret) { + fprintf(stderr, "Failed to disable %s: %s\n", sensor->name, strerror(ret)); + return 1; + } + + // sensors_close_1 is used in HAL versions >= 1.0. + ret = sensors_close_1(sensor_device); + if (ret) { + fprintf(stderr, "Failed to close the sensor device\n"); + return 1; + } + + return 0; +}
diff --git a/common/sensors_example/ndk-example-app.cpp b/common/sensors_example/ndk-example-app.cpp new file mode 100644 index 0000000..628068d --- /dev/null +++ b/common/sensors_example/ndk-example-app.cpp
@@ -0,0 +1,245 @@ +/* + * 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. + */ + +/* This file contains an example app that uses sensors NDK. + * It accepts a sensor type as an input argument and reads data from a + * sensor of that type. + * + * For any specified sensor type, there may be multiple such sensors defined + * in the HAL. Some or all of the defined sensors will actually be equipped. + * This test program will scan the HAL for the first equipped sensor of + * the specified sensor type and dump data from that sensor. + */ + +#include <getopt.h> +#include <math.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <android/looper.h> +#include <android/sensor.h> +#include <hardware/sensors.h> + +#define DEFAULT_SENSOR_TYPE SENSOR_TYPE_ACCELEROMETER + +// Structure to hold the decoded command line options +struct pgm_options { + int sensor_type; +}; + +// Be sure to keep the options for longopts and shortopts in the same order +// so that Usage() is correct. +static struct option longopts[] = { + {"help", no_argument, NULL, '?'}, + {"accel", no_argument, NULL, 'a'}, + {"temp", no_argument, NULL, 't'}, + {"light", no_argument, NULL, 'l'}, + {"orient", no_argument, NULL, 'o'}, + {"prox", no_argument, NULL, 'p'}, + {"motion", no_argument, NULL, 'm'}, + {NULL, 0, NULL, 0} +}; +static char shortopts[] = "?atlopm"; + +// Describes the options for this program. +void Usage(char *pgm_name) { + printf("Usage: %s [options...]\n", pgm_name); + printf("Exercises the sensors NDK by calling into the sensorserver.\n"); + printf("Options:\n"); + for (int i = 0; longopts[i].name; i++) { + printf(" --%-6s or -%c\n", longopts[i].name, shortopts[i]); + } +} + +// Processes all command line options. +// sets the options members for commnd line options +// returns (0) on success, -1 otherwise. +int ReadOpts(int argc, char **argv, struct pgm_options *options) { + int ch = 0; + + if (!options) { + fprintf(stderr, "Invalid options pointer\n"); + return 1; + } + + while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (ch) { + case 'a': + options->sensor_type = SENSOR_TYPE_ACCELEROMETER; + break; + case 't': + options->sensor_type = SENSOR_TYPE_TEMPERATURE; + break; + case 'l': + options->sensor_type = SENSOR_TYPE_LIGHT; + break; + case 'o': + options->sensor_type = SENSOR_TYPE_ORIENTATION; + break; + case 'p': + options->sensor_type = SENSOR_TYPE_PROXIMITY; + break; + case 'm': + options->sensor_type = SENSOR_TYPE_SIGNIFICANT_MOTION; + break; + default: + Usage(argv[0]); + return -1; + } + } + argc -= optind; + argv += optind; + return 0; +} + +// Prints data associated with each supported sensor type. +// Be sure to provide a case for each sensor type supported in +// the ReadOpts() function. +void DisplaySensorData(int sensor_type, const ASensorEvent *data) { + switch (sensor_type) { + case SENSOR_TYPE_PROXIMITY: + printf("Proximity distance: %f\n", data->distance); + break; + case SENSOR_TYPE_SIGNIFICANT_MOTION: + if (data->data[0] == 1) { + printf("Significant motion detected\n"); + } + break; + case SENSOR_TYPE_ACCELEROMETER: + printf("Acceleration: x = %f, y = %f, z = %f\n", + data->acceleration.x, data->acceleration.y, data->acceleration.z); + break; + case SENSOR_TYPE_TEMPERATURE: + printf("Temperature: %f\n", data->temperature); + break; + case SENSOR_TYPE_LIGHT: + printf("Light: %f\n", data->light); + break; + case SENSOR_TYPE_ORIENTATION: { + float heading = + atan2(static_cast<double>(data->magnetic.y), + static_cast<double>(data->magnetic.x)) * 180.0 / M_PI; + if (heading < 0.0) + heading += 360.0; + printf("Heading: %f, Orientation: x = %f, y = %f, z = %f\n", + heading, data->magnetic.x, data->magnetic.y, data->magnetic.z); + } + break; + } +} + +int main(int argc, char* argv[]) { + pgm_options options = {DEFAULT_SENSOR_TYPE}; + if (ReadOpts(argc, argv, &options) < 0) + return 1; + + const char kPackageName[] = "ndk-example-app"; + ASensorManager* sensor_manager = + ASensorManager_getInstanceForPackage(kPackageName); + if (!sensor_manager) { + fprintf(stderr, "Failed to get a sensor manager\n"); + return 1; + } + + ASensorList sensor_list = nullptr; + int sensor_count = ASensorManager_getSensorList(sensor_manager, &sensor_list); + printf("Found %d supported sensors\n", sensor_count); + for (int i = 0; i < sensor_count; i++) { + printf("HAL supports sensor %s\n", ASensor_getName(sensor_list[i])); + } + + const int kLooperId = 1; + ASensorEventQueue* queue = ASensorManager_createEventQueue( + sensor_manager, + ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS), + kLooperId, + NULL, /* no callback */ + NULL /* no private data for a callback */); + if (!queue) { + fprintf(stderr, "Failed to create a sensor event queue\n"); + return 1; + } + + // Find the first sensor of the specified type that can be opened + const int kTimeoutMicroSecs = 1000000; + const int kTimeoutMilliSecs = 1000; + ASensorRef sensor = nullptr; + bool sensor_found = false; + for (int i = 0; i < sensor_count; i++) { + sensor = sensor_list[i]; + if (ASensor_getType(sensor) != options.sensor_type) + continue; + if (ASensorEventQueue_enableSensor(queue, sensor) < 0) + continue; + if (ASensorEventQueue_setEventRate(queue, sensor, kTimeoutMicroSecs) < 0) { + fprintf(stderr, "Failed to set the %s sample rate\n", + ASensor_getName(sensor)); + return 1; + } + + // Found an equipped sensor of the specified type. + sensor_found = true; + break; + } + + if (!sensor_found) { + fprintf(stderr, "No sensor of the specified type found\n"); + int ret = ASensorManager_destroyEventQueue(sensor_manager, queue); + if (ret < 0) + fprintf(stderr, "Failed to destroy event queue: %s\n", strerror(-ret)); + return 1; + } + printf("\nSensor %s activated\n", ASensor_getName(sensor)); + + const int kNumEvents = 1; + const int kNumSamples = 10; + const int kWaitTimeSecs = 1; + for (int i = 0; i < kNumSamples; i++) { + ASensorEvent data[kNumEvents]; + memset(data, 0, sizeof(data)); + int ident = ALooper_pollAll( + kTimeoutMilliSecs, + NULL /* no output file descriptor */, + NULL /* no output event */, + NULL /* no output data */); + if (ident != kLooperId) { + fprintf(stderr, "Incorrect Looper ident read from poll.\n"); + continue; + } + if (ASensorEventQueue_getEvents(queue, data, kNumEvents) <= 0) { + fprintf(stderr, "Failed to read data from the sensor.\n"); + continue; + } + + DisplaySensorData(options.sensor_type, data); + sleep(kWaitTimeSecs); + } + + int ret = ASensorEventQueue_disableSensor(queue, sensor); + if (ret < 0) { + fprintf(stderr, "Failed to disable %s: %s\n", + ASensor_getName(sensor), strerror(-ret)); + } + + ret = ASensorManager_destroyEventQueue(sensor_manager, queue); + if (ret < 0) { + fprintf(stderr, "Failed to destroy event queue: %s\n", strerror(-ret)); + return 1; + } + + return 0; +}
diff --git a/common/sensors_example/sensors_hal.cpp b/common/sensors_example/sensors_hal.cpp new file mode 100644 index 0000000..5b89e59 --- /dev/null +++ b/common/sensors_example/sensors_hal.cpp
@@ -0,0 +1,179 @@ +/* + * 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 "sensors_hal.h" + +#include <errno.h> +#include <string.h> + +#include "example_sensors.h" + +SensorContext::SensorContext(const hw_module_t* module) { + memset(&device, 0, sizeof(device)); + + device.common.tag = HARDWARE_DEVICE_TAG; + device.common.version = SENSORS_DEVICE_API_VERSION_1_0; + device.common.module = const_cast<hw_module_t*>(module); + device.common.close = CloseWrapper; + device.activate = ActivateWrapper; + device.setDelay = SetDelayWrapper; + device.poll = PollEventsWrapper; + device.batch = BatchWrapper; + device.flush = FlushWrapper; + + sensors[AvailableSensors::kAccelerometer] = new Accelerometer(); + sensors[AvailableSensors::kCustom] = new CustomSensor(); +} + +SensorContext::~SensorContext() { + for (int i = 0; i < AvailableSensors::kNumSensors; i++) { + if (sensors[i]) delete sensors[i]; + } +} + +int SensorContext::activate(int handle, int enabled) { + return sensors[handle]->activate(handle, enabled); +} + +int SensorContext::setDelay(int handle, int64_t ns) { + return sensors[handle]->setDelay(handle, ns); +} + +int SensorContext::pollEvents(sensors_event_t* data, int count) { + return sensors[0]->pollEvents(data, count); +} + +int SensorContext::batch(int handle, int flags, + int64_t period_ns, int64_t timeout) { + return sensors[handle]->batch(handle, flags, period_ns, timeout); +} + +int SensorContext::flush(int handle) { + return sensors[handle]->flush(handle); +} + +// static +int SensorContext::CloseWrapper(hw_device_t* dev) { + SensorContext* sensor_context = reinterpret_cast<SensorContext*>(dev); + if (sensor_context) { + delete sensor_context; + } + return 0; +} + +// static +int SensorContext::ActivateWrapper(sensors_poll_device_t* dev, + int handle, int enabled) { + return reinterpret_cast<SensorContext*>(dev)->activate(handle, enabled); +} + +// static +int SensorContext::SetDelayWrapper(sensors_poll_device_t* dev, + int handle, int64_t ns) { + return reinterpret_cast<SensorContext*>(dev)->setDelay(handle, ns); +} + +// static +int SensorContext::PollEventsWrapper(sensors_poll_device_t* dev, + sensors_event_t* data, int count) { + return reinterpret_cast<SensorContext*>(dev)->pollEvents(data, count); +} + +// static +int SensorContext::BatchWrapper(sensors_poll_device_1_t* dev, int handle, + int flags, int64_t period_ns, int64_t timeout) { + return reinterpret_cast<SensorContext*>(dev)->batch(handle, flags, period_ns, + timeout); +} + +// static +int SensorContext::FlushWrapper(sensors_poll_device_1_t* dev, + int handle) { + return reinterpret_cast<SensorContext*>(dev)->flush(handle); +} + + +static int open_sensors(const struct hw_module_t* module, + const char* id, struct hw_device_t** device) { + SensorContext* ctx = new SensorContext(module); + *device = &ctx->device.common; + + return 0; +} + +static struct hw_module_methods_t sensors_module_methods = { + .open = open_sensors, +}; + +static struct sensor_t kSensorList[] = { + { .name = "Broken Accelerometer", + .vendor = "Unknown", + .version = 1, + .handle = SensorContext::AvailableSensors::kAccelerometer, + .type = SENSOR_TYPE_ACCELEROMETER, + .maxRange = static_cast<float>(Accelerometer::kMaxRange), + .resolution = 100.0f, + .power = 0.0f, + .minDelay = 10000, + .fifoReservedEventCount = 0, + .fifoMaxEventCount = 0, + .stringType = SENSOR_STRING_TYPE_ACCELEROMETER, + .requiredPermission = "", + .maxDelay = 0, + .flags = SENSOR_FLAG_CONTINUOUS_MODE, + .reserved = {}, + }, + { .name = "Custom Hour of Day Sensor", + .vendor = "Unknown", + .version = 1, + .handle = SensorContext::AvailableSensors::kCustom, + .type = SENSOR_TYPE_CUSTOM, + .maxRange = 24.0f, + .resolution = 1.0f, + .power = 0.0f, + .minDelay = 1, + .fifoReservedEventCount = 0, + .fifoMaxEventCount = 0, + .stringType = SENSOR_STRING_TYPE_CUSTOM, + .requiredPermission = "", + .maxDelay = 0, + .flags = SENSOR_FLAG_CONTINUOUS_MODE, + .reserved = {}, + }, +}; + +static int get_sensors_list(struct sensors_module_t* module, + struct sensor_t const** list) { + if (!list) return 0; + *list = kSensorList; + return sizeof(kSensorList) / sizeof(kSensorList[0]); +} + +struct sensors_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = SENSORS_HARDWARE_MODULE_ID, + .name = "Example Sensor Module", + .author = "Google", + .methods = &sensors_module_methods, + .dso = NULL, + .reserved = {0}, + }, + .get_sensors_list = get_sensors_list, + .set_operation_mode = NULL +};
diff --git a/common/sensors_example/sensors_hal.h b/common/sensors_example/sensors_hal.h new file mode 100644 index 0000000..e563d7e --- /dev/null +++ b/common/sensors_example/sensors_hal.h
@@ -0,0 +1,70 @@ +/* + * 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 SENSORS_SENSORS_HAL_H +#define SENSORS_SENSORS_HAL_H + +/* + * SENSOR_TYPE_CUSTOM + * The custom sensor type must have a integer constant starting from 27. + * The string name of the custom type must also be defined. + */ +#define SENSOR_TYPE_CUSTOM (27) +#define SENSOR_STRING_TYPE_CUSTOM "brillo.custom" + +#include <stdint.h> + +#include <hardware/sensors.h> + +#include "example_sensors.h" + +class SensorContext { + public: + sensors_poll_device_1_t device; + + SensorContext(const hw_module_t* module); + ~SensorContext(); + + enum AvailableSensors { + kAccelerometer, + kCustom, + kNumSensors + }; + + private: + int activate(int handle, int enabled); + int setDelay(int handle, int64_t ns); + int pollEvents(sensors_event_t* data, int count); + int batch(int handle, int flags, int64_t period_ns, int64_t timeout); + int flush(int handle); + + // The wrapper pass through to the specific instantiation of + // the SensorContext. + static int CloseWrapper(hw_device_t* dev); + static int ActivateWrapper(sensors_poll_device_t* dev, int handle, + int enabled); + static int SetDelayWrapper(sensors_poll_device_t* dev, int handle, + int64_t ns); + static int PollEventsWrapper(sensors_poll_device_t* dev, + sensors_event_t* data, int count); + static int BatchWrapper(sensors_poll_device_1_t* dev, int handle, int flags, + int64_t period_ns, int64_t timeout); + static int FlushWrapper(sensors_poll_device_1_t* dev, int handle); + + SensorBase* sensors[AvailableSensors::kNumSensors]; +}; + +#endif // SENSORS_SENSORS_HAL_H
diff --git a/common/service_example/Android.mk b/common/service_example/Android.mk new file mode 100644 index 0000000..31165a9 --- /dev/null +++ b/common/service_example/Android.mk
@@ -0,0 +1,52 @@ +# 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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := brillo_example_service +LOCAL_SRC_FILES := \ + brillo_example_service.cpp \ + android/brillo/example/IAlertCallback.aidl \ + android/brillo/example/IExampleService.aidl +LOCAL_AIDL_INCLUDES := $(LOCAL_PATH) +LOCAL_CFLAGS := -Wall +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libbrillo \ + libbrillo-binder \ + libchrome \ + libutils +# Uncomment the following line to have this service started automatically on +# boot by init. +#LOCAL_INIT_RC := example_service.rc +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := brillo_example_client +LOCAL_SRC_FILES := \ + brillo_example_client.cpp \ + android/brillo/example/IAlertCallback.aidl \ + android/brillo/example/IExampleService.aidl +LOCAL_AIDL_INCLUDES := $(LOCAL_PATH) +LOCAL_CFLAGS := -Wall +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libbrillo \ + libbrillo-binder \ + libchrome \ + libcutils \ + libutils +include $(BUILD_EXECUTABLE)
diff --git a/common/service_example/README.md b/common/service_example/README.md new file mode 100644 index 0000000..47838d6 --- /dev/null +++ b/common/service_example/README.md
@@ -0,0 +1,70 @@ +Brillo Example Service +====================== + +This folder contains an example Brillo service and client that use AIDL. This +example is intended to show: + +1. Implementing an interface defined in AIDL. +2. Registering a service with the service manager. +3. Calling a service from a client. +4. Registering a callback object from the client so the service can talk back + to the client. + +Folder layout +------------- + +* android/brillo/example/IExampleService.aidl - AIDL interface for an example + service that accepts a string and logs it. The service also provides an + interface to let a client be notified once it has called the service n + times. +* android/brillo/example/IAlertCallback.aidl - AIDL interface for the callback + object that the client registers with the service. Note the use of the + keyword oneway in the interface. This keyword prevents the service from + blocking on the client handling the callback. +* brillo_example_service.cpp - The implementation of the AIDL service + interface. The service has to implement + android::brillo::example::BnExampleService which is done in ExampleService + class. The service also illustrates how to use brillo::BinderWatcher with a + message loop to drive the service. +* brillo_example_client.cpp - Client application that calls the service and + receives callbacks. The client implements the callback object in + AlertCallback. AlertCallback by extending + android::brillo::example::BnAlertCallback. The client also uses + brillo::BinderWatcher and message loops to receive callbacks. + +The brillo_example_client reads std::in and passes that information to the +brillo_example_service which logs it using logcat. + +Usage +----- + +To use this example service/client: + +1. Open two terminals. +2. In the first one, run + + adb shell + + and then start the example service in the background (not required if the + service is started by init) + + brillo_example_service & + +3. Start logcat in the second terminal + + adb logcat + +4. In the first terminal, start the example client as root. + + brillo_example_client + +5. If you type "foo" in the first terminal followed by a newline, you should see + the following text in the logcat output: + + brillo_example_service: foo + +6. After 3 messages have been sent to the service by the client, the following + statement will be logged by the client. + + brillo_example_client: Received Callback from service: Received 3 log messages. +
diff --git a/common/service_example/android/brillo/example/IAlertCallback.aidl b/common/service_example/android/brillo/example/IAlertCallback.aidl new file mode 100644 index 0000000..a3eb9ee --- /dev/null +++ b/common/service_example/android/brillo/example/IAlertCallback.aidl
@@ -0,0 +1,25 @@ +/* + * 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. + */ + +package android.brillo.example; + +// Interface for a callback object that is to be registered with +// IExampleService. +interface IAlertCallback { + // This should be a oneway call since we don't want services to be blocked on + // clients. + oneway void OnNLogEventsReceivedCallback(int n); +}
diff --git a/common/service_example/android/brillo/example/IExampleService.aidl b/common/service_example/android/brillo/example/IExampleService.aidl new file mode 100644 index 0000000..d478abd --- /dev/null +++ b/common/service_example/android/brillo/example/IExampleService.aidl
@@ -0,0 +1,31 @@ +/* + * 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. + */ + +package android.brillo.example; + +import android.brillo.example.IAlertCallback; + +// Interface for an example service that accepts a string and logs it using +// logcat. It also provides a method to register a callback. +interface IExampleService { + // Registers a callback object of type IAlertCallback with the service. Once + // the service has recieved n calls to Log, then the method + // OnNEventsReceivedCallback on the callback object will be called. + void RegisterCallback(IAlertCallback callback, int n); + + // Logs a string. + void Log(String s); +}
diff --git a/common/service_example/brillo_example_client.cpp b/common/service_example/brillo_example_client.cpp new file mode 100644 index 0000000..cb49720 --- /dev/null +++ b/common/service_example/brillo_example_client.cpp
@@ -0,0 +1,85 @@ +/* + * 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 <iostream> +#include <string> + +#include <base/at_exit.h> +#include <base/bind.h> +#include <base/logging.h> +#include <base/message_loop/message_loop.h> +#include <binder/IInterface.h> +#include <binder/IServiceManager.h> +#include <brillo/binder_watcher.h> +#include <brillo/message_loops/base_message_loop.h> +#include <brillo/syslog_logging.h> +#include <utils/String16.h> + +#include "android/brillo/example/BnAlertCallback.h" +#include "android/brillo/example/IExampleService.h" + +namespace android { + +class AlertCallback : public android::brillo::example::BnAlertCallback { + public: + binder::Status OnNLogEventsReceivedCallback(int n) { + LOG(INFO) << "Received Callback from service: Received " << n << " log " + << "messages."; + return binder::Status::ok(); + } +}; + +} // namespace android + +// A function to read user input on the console and pass it along to the example +// service for logging. +void StdinCallback( + android::sp<android::brillo::example::IExampleService> service) { + std::string line; + std::getline(std::cin, line); + service->Log(android::String16(line.c_str())); +} + +int main() { + // TODO(ralphnathan): Hide this in brillo::BaseMessageLoop. + base::AtExitManager at_exit_manager; + brillo::InitLog(brillo::kLogToSyslog); + // Get handle for the example service. + android::sp<android::brillo::example::IExampleService> service; + android::status_t status = android::getService( + android::String16("android.brillo.example.ExampleService"), &service); + CHECK(status == android::OK) << + "Failed to get IExampleService binder from service manager!"; + + // Register a callback object with the service. + android::sp<android::AlertCallback> cbo = new android::AlertCallback(); + service->RegisterCallback(cbo, 3); + + // Create a message loop. + base::MessageLoopForIO message_loop_for_io; + brillo::BaseMessageLoop message_loop{&message_loop_for_io}; + + // Initialize a binder watcher. + brillo::BinderWatcher watcher(&message_loop); + watcher.Init(); + + // Poll stdin. + base::Closure stdin_callback = base::Bind(&StdinCallback, service); + message_loop.WatchFileDescriptor( + STDIN_FILENO, brillo::BaseMessageLoop::kWatchRead, true, stdin_callback); + // Start the message loop. + message_loop.Run(); +}
diff --git a/common/service_example/brillo_example_service.cpp b/common/service_example/brillo_example_service.cpp new file mode 100644 index 0000000..fd24a7e --- /dev/null +++ b/common/service_example/brillo_example_service.cpp
@@ -0,0 +1,81 @@ +/* + * 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 <base/at_exit.h> +#include <base/logging.h> +#include <base/message_loop/message_loop.h> +#include <binder/IServiceManager.h> +#include <binder/Status.h> +#include <brillo/binder_watcher.h> +#include <brillo/message_loops/base_message_loop.h> +#include <brillo/syslog_logging.h> +#include <utils/String16.h> + +#include "android/brillo/example/BnExampleService.h" +#include "android/brillo/example/IAlertCallback.h" + +namespace android { + +class ExampleService : public android::brillo::example::BnExampleService { + public: + binder::Status Log(const String16& s) { + LOG(INFO) << String8(s).string(); + count_++; + if (count_ == max_events_) { + cbo_->OnNLogEventsReceivedCallback(count_); + count_ = 0; + } + return binder::Status::ok(); + } + + binder::Status RegisterCallback( + const sp<android::brillo::example::IAlertCallback>& callback, int n) { + cbo_ = callback; + max_events_ = n; + count_ = 0; + return binder::Status::ok(); + } + + private: + sp<android::brillo::example::IAlertCallback> cbo_; + int count_ = 0; + int max_events_ = 0; +}; + +} // namespace android + +int main() { + // TODO(ralphnathan): Hide this in brillo::BaseMessageLoop. + base::AtExitManager at_exit_manager; + brillo::InitLog(brillo::kLogToSyslog); + // Register service with the service manager. + android::status_t status = android::defaultServiceManager()->addService( + android::String16("android.brillo.example.ExampleService"), + new android::ExampleService()); + CHECK(status == android::OK) << + "Failed to get IExampleService binder from service manager!"; + + // Create a message loop. + base::MessageLoopForIO message_loop_for_io; + brillo::BaseMessageLoop message_loop{&message_loop_for_io}; + + // Initialize a binder watcher. + brillo::BinderWatcher watcher(&message_loop); + watcher.Init(); + + // Run the message loop. + message_loop.Run(); +}
diff --git a/common/service_example/example_service.rc b/common/service_example/example_service.rc new file mode 100644 index 0000000..79200e8 --- /dev/null +++ b/common/service_example/example_service.rc
@@ -0,0 +1,4 @@ +service example_service /system/bin/brillo_example_service + class main + user system + group system
diff --git a/common/service_example/sepolicy/brillo_example_service.te b/common/service_example/sepolicy/brillo_example_service.te new file mode 100644 index 0000000..9586a40 --- /dev/null +++ b/common/service_example/sepolicy/brillo_example_service.te
@@ -0,0 +1,12 @@ +# Domain for the Brillo example service. +type brillo_example_service, domain; +type brillo_example_service_exec, exec_type, file_type; + +# Allow domain transition from init, and access to D-Bus and Binder. +brillo_domain(brillo_example_service) + +# Allow the example service to add itself to service_manager. +allow brillo_example_service brilloexampleservice:service_manager add; + +# Allow crash_reporter access to core dump files. +allow_crash_reporter(brillo_example_service)
diff --git a/common/service_example/sepolicy/file_contexts b/common/service_example/sepolicy/file_contexts new file mode 100644 index 0000000..017e759 --- /dev/null +++ b/common/service_example/sepolicy/file_contexts
@@ -0,0 +1,3 @@ +# Associate the example service's executable with the domain defined in +# brillo_example_service.te. +/system/bin/brillo_example_service u:object_r:brillo_example_service_exec:s0
diff --git a/common/service_example/sepolicy/service.te b/common/service_example/sepolicy/service.te new file mode 100644 index 0000000..6544432 --- /dev/null +++ b/common/service_example/sepolicy/service.te
@@ -0,0 +1,2 @@ +# Add a context for the Brillo example service. +type brilloexampleservice, service_manager_type;
diff --git a/common/service_example/sepolicy/service_contexts b/common/service_example/sepolicy/service_contexts new file mode 100644 index 0000000..d78561e --- /dev/null +++ b/common/service_example/sepolicy/service_contexts
@@ -0,0 +1,3 @@ +# Associate the Brillo example service's name (as defined when it is added to +# the service manager) with a context. +android.brillo.example.ExampleService u:object_r:brilloexampleservice:s0