upload android base code part4

This commit is contained in:
August 2018-08-08 17:00:29 +08:00
parent b9e30e05b1
commit 78ea2404cd
23455 changed files with 5250148 additions and 0 deletions

View file

@ -0,0 +1,38 @@
# Copyright 2016 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
APF_CFLAGS := -DAPF_FRAME_HEADER_SIZE=14 \
-Wall \
-Werror
include $(CLEAR_VARS)
LOCAL_INCLUDES += $(LOCAL_PATH)
LOCAL_CFLAGS += $(APF_CFLAGS)
LOCAL_SRC_FILES += apf_interpreter.c
LOCAL_MODULE:= libapf
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CFLAGS += $(APF_CFLAGS)
LOCAL_SRC_FILES += apf_disassembler.c
LOCAL_MODULE := apf_disassembler
LOCAL_MODULE_TAGS := debug
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_CFLAGS += $(APF_CFLAGS)
LOCAL_SRC_FILES += apf_run.c apf_interpreter.c
LOCAL_MODULE := apf_run
LOCAL_MODULE_TAGS := debug
include $(BUILD_HOST_EXECUTABLE)

View file

@ -0,0 +1,201 @@
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.

View file

@ -0,0 +1,6 @@
per-file Android.bp = build.master@android.com
ek@google.com
hugobenichi@google.com
lorenzo@google.com
pauljensen@google.com

View file

@ -0,0 +1,161 @@
/*
* 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.
*/
// A brief overview of APF:
//
// APF machine is composed of:
// 1. A read-only program consisting of bytecodes as described below.
// 2. Two 32-bit registers, called R0 and R1.
// 3. Sixteen 32-bit memory slots.
// 4. A read-only packet.
// The program is executed by the interpreter below and parses the packet
// to determine if the application processor (AP) should be woken up to
// handle the packet or if can be dropped.
//
// APF bytecode description:
//
// The APF interpreter uses big-endian byte order for loads from the packet
// and for storing immediates in instructions.
//
// Each instruction starts with a byte composed of:
// Top 5 bits form "opcode" field, see *_OPCODE defines below.
// Next 2 bits form "size field", which indicate the length of an immediate
// value which follows the first byte. Values in this field:
// 0 => immediate value is 0 and no bytes follow.
// 1 => immediate value is 1 byte big.
// 2 => immediate value is 2 bytes big.
// 3 => immediate value is 4 bytes big.
// Bottom bit forms "register" field, which indicates which register this
// instruction operates on.
//
// There are three main categories of instructions:
// Load instructions
// These instructions load byte(s) of the packet into a register.
// They load either 1, 2 or 4 bytes, as determined by the "opcode" field.
// They load into the register specified by the "register" field.
// The immediate value that follows the first byte of the instruction is
// the byte offset from the begining of the packet to load from.
// There are "indexing" loads which add the value in R1 to the byte offset
// to load from. The "opcode" field determines which loads are "indexing".
// Arithmetic instructions
// These instructions perform simple operations, like addition, on register
// values. The result of these instructions is always written into R0. One
// argument of the arithmetic operation is R0's value. The other argument
// of the arithmetic operation is determined by the "register" field:
// If the "register" field is 0 then the immediate value following
// the first byte of the instruction is used as the other argument
// to the arithmetic operation.
// If the "register" field is 1 then R1's value is used as the other
// argument to the arithmetic operation.
// Conditional jump instructions
// These instructions compare register R0's value with another value, and if
// the comparison succeeds, jump (i.e. adjust the program counter). The
// immediate value that follows the first byte of the instruction
// represents the jump target offset, i.e. the value added to the program
// counter if the comparison succeeds. The other value compared is
// determined by the "register" field:
// If the "register" field is 0 then another immediate value
// follows the jump target offset. This immediate value is of the
// same size as the jump target offset, and represents the value
// to compare against.
// If the "register" field is 1 then register R1's value is
// compared against.
// The type of comparison (e.g. equal to, greater than etc) is determined
// by the "opcode" field. The comparison interprets both values being
// compared as unsigned values.
//
// Miscellaneous details:
//
// Pre-filled memory slot values
// When the APF program begins execution, three of the sixteen memory slots
// are pre-filled by the interpreter with values that may be useful for
// programs:
// Slot #13 is filled with the IPv4 header length. This value is calculated
// by loading the first byte of the IPv4 header and taking the
// bottom 4 bits and multiplying their value by 4. This value is
// set to zero if the first 4 bits after the link layer header are
// not 4, indicating not IPv4.
// Slot #14 is filled with size of the packet in bytes, including the
// link-layer header if any.
// Slot #15 is filled with the filter age in seconds. This is the number of
// seconds since the AP send the program to the chipset. This may
// be used by filters that should have a particular lifetime. For
// example, it can be used to rate-limit particular packets to one
// every N seconds.
// Special jump targets:
// When an APF program executes a jump to the byte immediately after the last
// byte of the progam (i.e., one byte past the end of the program), this
// signals the program has completed and determined the packet should be
// passed to the AP.
// When an APF program executes a jump two bytes past the end of the program,
// this signals the program has completed and determined the packet should
// be dropped.
// Jump if byte sequence doesn't match:
// This is a special instruction to facilitate matching long sequences of
// bytes in the packet. Initially it is encoded like a conditional jump
// instruction with two exceptions:
// The first byte of the instruction is always followed by two immediate
// fields: The first immediate field is the jump target offset like other
// conditional jump instructions. The second immediate field specifies the
// number of bytes to compare.
// These two immediate fields are followed by a sequence of bytes. These
// bytes are compared with the bytes in the packet starting from the
// position specified by the value of the register specified by the
// "register" field of the instruction.
// Number of memory slots, see ldm/stm instructions.
#define MEMORY_ITEMS 16
// Upon program execution starting some memory slots are prefilled:
#define MEMORY_OFFSET_IPV4_HEADER_SIZE 13 // 4*([APF_FRAME_HEADER_SIZE]&15)
#define MEMORY_OFFSET_PACKET_SIZE 14 // Size of packet in bytes.
#define MEMORY_OFFSET_FILTER_AGE 15 // Age since filter installed in seconds.
// Leave 0 opcode unused as it's a good indicator of accidental incorrect execution (e.g. data).
#define LDB_OPCODE 1 // Load 1 byte from immediate offset, e.g. "ldb R0, [5]"
#define LDH_OPCODE 2 // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]"
#define LDW_OPCODE 3 // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]"
#define LDBX_OPCODE 4 // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0"
#define LDHX_OPCODE 5 // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0"
#define LDWX_OPCODE 6 // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0"
#define ADD_OPCODE 7 // Add, e.g. "add R0,5"
#define MUL_OPCODE 8 // Multiply, e.g. "mul R0,5"
#define DIV_OPCODE 9 // Divide, e.g. "div R0,5"
#define AND_OPCODE 10 // And, e.g. "and R0,5"
#define OR_OPCODE 11 // Or, e.g. "or R0,5"
#define SH_OPCODE 12 // Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right)
#define LI_OPCODE 13 // Load immediate, e.g. "li R0,5" (immediate encoded as signed value)
#define JMP_OPCODE 14 // Unconditional jump, e.g. "jmp label"
#define JEQ_OPCODE 15 // Compare equal and branch, e.g. "jeq R0,5,label"
#define JNE_OPCODE 16 // Compare not equal and branch, e.g. "jne R0,5,label"
#define JGT_OPCODE 17 // Compare greater than and branch, e.g. "jgt R0,5,label"
#define JLT_OPCODE 18 // Compare less than and branch, e.g. "jlt R0,5,label"
#define JSET_OPCODE 19 // Compare any bits set and branch, e.g. "jset R0,5,label"
#define JNEBS_OPCODE 20 // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455"
#define EXT_OPCODE 21 // Immediate value is one of *_EXT_OPCODE
// Extended opcodes. These all have an opcode of EXT_OPCODE
// and specify the actual opcode in the immediate field.
#define LDM_EXT_OPCODE 0 // Load from memory, e.g. "ldm R0,5"
// Values 0-15 represent loading the different memory slots.
#define STM_EXT_OPCODE 16 // Store to memory, e.g. "stm R0,5"
// Values 16-31 represent storing to the different memory slots.
#define NOT_EXT_OPCODE 32 // Not, e.g. "not R0"
#define NEG_EXT_OPCODE 33 // Negate, e.g. "neg R0"
#define SWAP_EXT_OPCODE 34 // Swap, e.g. "swap R0,R1"
#define MOV_EXT_OPCODE 35 // Move, e.g. "move R0,R1"
#define EXTRACT_OPCODE(i) (((i) >> 3) & 31)
#define EXTRACT_REGISTER(i) ((i) & 1)
#define EXTRACT_IMM_LENGTH(i) (((i) >> 1) & 3)

View file

@ -0,0 +1,217 @@
/*
* 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 <stdint.h>
#include <stdio.h>
#include "apf.h"
// If "c" is of an unsigned type, generate a compile warning that gets promoted to an error.
// This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding
// superfluous ">= 0" with unsigned expressions generates compile warnings.
#define ENFORCE_UNSIGNED(c) ((c)==(uint32_t)(c))
static void print_opcode(const char* opcode) {
printf("%-6s", opcode);
}
// Mapping from opcode number to opcode name.
static const char* opcode_names [] = {
[LDB_OPCODE] = "ldb",
[LDH_OPCODE] = "ldh",
[LDW_OPCODE] = "ldw",
[LDBX_OPCODE] = "ldb",
[LDHX_OPCODE] = "ldh",
[LDWX_OPCODE] = "ldw",
[ADD_OPCODE] = "add",
[MUL_OPCODE] = "mul",
[DIV_OPCODE] = "div",
[AND_OPCODE] = "and",
[OR_OPCODE] = "or",
[SH_OPCODE] = "sh",
[LI_OPCODE] = "li",
[JMP_OPCODE] = "jmp",
[JEQ_OPCODE] = "jeq",
[JNE_OPCODE] = "jne",
[JGT_OPCODE] = "jgt",
[JLT_OPCODE] = "jlt",
[JSET_OPCODE] = "jset",
[JNEBS_OPCODE] = "jnebs",
};
static void print_jump_target(uint32_t target, uint32_t program_len) {
if (target == program_len) {
printf("pass");
} else if (target == program_len + 1) {
printf("drop");
} else {
printf("%u", target);
}
}
// Disassembles an APF program. A hex dump of the program is supplied on stdin.
//
// NOTE: This is a simple debugging tool not meant for shipping or production use. It is by no
// means hardened against malicious input and contains known vulnerabilities.
//
// Example usage:
// adb shell dumpsys wifi ipmanager | sed '/Last program:/,+1!d;/Last program:/d;s/[ ]*//' | out/host/linux-x86/bin/apf_disassembler
int main(void) {
uint32_t program_len = 0;
uint8_t program[10000];
// Read in hex program bytes
int byte;
while (scanf("%2x", &byte) == 1 && program_len < sizeof(program)) {
program[program_len++] = byte;
}
for (uint32_t pc = 0; pc < program_len;) {
printf("%8u: ", pc);
const uint8_t bytecode = program[pc++];
const uint32_t opcode = EXTRACT_OPCODE(bytecode);
#define PRINT_OPCODE() print_opcode(opcode_names[opcode])
const uint32_t reg_num = EXTRACT_REGISTER(bytecode);
// All instructions have immediate fields, so load them now.
const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode);
uint32_t imm = 0;
int32_t signed_imm = 0;
if (len_field != 0) {
const uint32_t imm_len = 1 << (len_field - 1);
uint32_t i;
for (i = 0; i < imm_len && pc < program_len; i++)
imm = (imm << 8) | program[pc++];
// Sign extend imm into signed_imm.
signed_imm = imm << ((4 - imm_len) * 8);
signed_imm >>= (4 - imm_len) * 8;
}
switch (opcode) {
case LDB_OPCODE:
case LDH_OPCODE:
case LDW_OPCODE:
PRINT_OPCODE();
printf("r%d, [%u]", reg_num, imm);
break;
case LDBX_OPCODE:
case LDHX_OPCODE:
case LDWX_OPCODE:
PRINT_OPCODE();
printf("r%d, [%u+r1]", reg_num, imm);
break;
case JMP_OPCODE:
PRINT_OPCODE();
print_jump_target(pc + imm, program_len);
break;
case JEQ_OPCODE:
case JNE_OPCODE:
case JGT_OPCODE:
case JLT_OPCODE:
case JSET_OPCODE:
case JNEBS_OPCODE: {
PRINT_OPCODE();
printf("r0, ");
// Load second immediate field.
uint32_t cmp_imm = 0;
if (reg_num == 1) {
printf("r1, ");
} else if (len_field == 0) {
printf("0, ");
} else {
uint32_t cmp_imm_len = 1 << (len_field - 1);
uint32_t i;
for (i = 0; i < cmp_imm_len && pc < program_len; i++)
cmp_imm = (cmp_imm << 8) | program[pc++];
printf("0x%x, ", cmp_imm);
}
if (opcode == JNEBS_OPCODE) {
print_jump_target(pc + imm + cmp_imm, program_len);
printf(", ");
while (cmp_imm--)
printf("%02x", program[pc++]);
} else {
print_jump_target(pc + imm, program_len);
}
break;
}
case ADD_OPCODE:
case SH_OPCODE:
PRINT_OPCODE();
if (reg_num) {
printf("r0, r1");
} else {
printf("r0, %d", signed_imm);
}
break;
case MUL_OPCODE:
case DIV_OPCODE:
case AND_OPCODE:
case OR_OPCODE:
PRINT_OPCODE();
if (reg_num) {
printf("r0, r1");
} else {
printf("r0, %u", imm);
}
break;
case LI_OPCODE:
PRINT_OPCODE();
printf("r%d, %d", reg_num, signed_imm);
break;
case EXT_OPCODE:
if (
// If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result,
// instead just enforce that imm is unsigned (so it's always greater or equal to 0).
#if LDM_EXT_OPCODE == 0
ENFORCE_UNSIGNED(imm) &&
#else
imm >= LDM_EXT_OPCODE &&
#endif
imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
print_opcode("ldm");
printf("r%d, m[%u]", reg_num, imm - LDM_EXT_OPCODE);
} else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
print_opcode("stm");
printf("r%d, m[%u]", reg_num, imm - STM_EXT_OPCODE);
} else switch (imm) {
case NOT_EXT_OPCODE:
print_opcode("not");
printf("r%d", reg_num);
break;
case NEG_EXT_OPCODE:
print_opcode("neg");
printf("r%d", reg_num);
break;
case SWAP_EXT_OPCODE:
print_opcode("swap");
break;
case MOV_EXT_OPCODE:
print_opcode("mov");
printf("r%d, r%d", reg_num, reg_num ^ 1);
break;
default:
printf("unknown_ext %u", imm);
break;
}
break;
// Unknown opcode
default:
printf("unknown %u", opcode);
break;
}
printf("\n");
}
return 0;
}

View file

@ -0,0 +1,278 @@
/*
* 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 "apf_interpreter.h"
#include <string.h> // For memcmp
#include "apf.h"
// Return code indicating "packet" should accepted.
#define PASS_PACKET 1
// Return code indicating "packet" should be dropped.
#define DROP_PACKET 0
// Verify an internal condition and accept packet if it fails.
#define ASSERT_RETURN(c) if (!(c)) return PASS_PACKET
// If "c" is of an unsigned type, generate a compile warning that gets promoted to an error.
// This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding
// superfluous ">= 0" with unsigned expressions generates compile warnings.
#define ENFORCE_UNSIGNED(c) ((c)==(uint32_t)(c))
/**
* Runs a packet filtering program over a packet.
*
* @param program the program bytecode.
* @param program_len the length of {@code apf_program} in bytes.
* @param packet the packet bytes, starting from the 802.3 header and not
* including any CRC bytes at the end.
* @param packet_len the length of {@code packet} in bytes.
* @param filter_age the number of seconds since the filter was programmed.
*
* @return non-zero if packet should be passed to AP, zero if
* packet should be dropped.
*/
int accept_packet(const uint8_t* program, uint32_t program_len,
const uint8_t* packet, uint32_t packet_len,
uint32_t filter_age) {
// Is offset within program bounds?
#define IN_PROGRAM_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < program_len)
// Is offset within packet bounds?
#define IN_PACKET_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < packet_len)
// Accept packet if not within program bounds
#define ASSERT_IN_PROGRAM_BOUNDS(p) ASSERT_RETURN(IN_PROGRAM_BOUNDS(p))
// Accept packet if not within packet bounds
#define ASSERT_IN_PACKET_BOUNDS(p) ASSERT_RETURN(IN_PACKET_BOUNDS(p))
// Program counter.
uint32_t pc = 0;
// Accept packet if not within program or not ahead of program counter
#define ASSERT_FORWARD_IN_PROGRAM(p) ASSERT_RETURN(IN_PROGRAM_BOUNDS(p) && (p) >= pc)
// Memory slot values.
uint32_t memory[MEMORY_ITEMS] = {};
// Fill in pre-filled memory slot values.
memory[MEMORY_OFFSET_PACKET_SIZE] = packet_len;
memory[MEMORY_OFFSET_FILTER_AGE] = filter_age;
ASSERT_IN_PACKET_BOUNDS(APF_FRAME_HEADER_SIZE);
// Only populate if IP version is IPv4.
if ((packet[APF_FRAME_HEADER_SIZE] & 0xf0) == 0x40) {
memory[MEMORY_OFFSET_IPV4_HEADER_SIZE] = (packet[APF_FRAME_HEADER_SIZE] & 15) * 4;
}
// Register values.
uint32_t registers[2] = {};
// Count of instructions remaining to execute. This is done to ensure an
// upper bound on execution time. It should never be hit and is only for
// safety. Initialize to the number of bytes in the program which is an
// upper bound on the number of instructions in the program.
uint32_t instructions_remaining = program_len;
do {
if (pc == program_len) {
return PASS_PACKET;
} else if (pc == (program_len + 1)) {
return DROP_PACKET;
}
ASSERT_IN_PROGRAM_BOUNDS(pc);
const uint8_t bytecode = program[pc++];
const uint32_t opcode = EXTRACT_OPCODE(bytecode);
const uint32_t reg_num = EXTRACT_REGISTER(bytecode);
#define REG (registers[reg_num])
#define OTHER_REG (registers[reg_num ^ 1])
// All instructions have immediate fields, so load them now.
const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode);
uint32_t imm = 0;
int32_t signed_imm = 0;
if (len_field != 0) {
const uint32_t imm_len = 1 << (len_field - 1);
ASSERT_FORWARD_IN_PROGRAM(pc + imm_len - 1);
uint32_t i;
for (i = 0; i < imm_len; i++)
imm = (imm << 8) | program[pc++];
// Sign extend imm into signed_imm.
signed_imm = imm << ((4 - imm_len) * 8);
signed_imm >>= (4 - imm_len) * 8;
}
switch (opcode) {
case LDB_OPCODE:
case LDH_OPCODE:
case LDW_OPCODE:
case LDBX_OPCODE:
case LDHX_OPCODE:
case LDWX_OPCODE: {
uint32_t offs = imm;
if (opcode >= LDBX_OPCODE) {
// Note: this can overflow and actually decrease offs.
offs += registers[1];
}
ASSERT_IN_PACKET_BOUNDS(offs);
uint32_t load_size;
switch (opcode) {
case LDB_OPCODE:
case LDBX_OPCODE:
load_size = 1;
break;
case LDH_OPCODE:
case LDHX_OPCODE:
load_size = 2;
break;
case LDW_OPCODE:
case LDWX_OPCODE:
load_size = 4;
break;
// Immediately enclosing switch statement guarantees
// opcode cannot be any other value.
}
const uint32_t end_offs = offs + (load_size - 1);
// Catch overflow/wrap-around.
ASSERT_RETURN(end_offs >= offs);
ASSERT_IN_PACKET_BOUNDS(end_offs);
uint32_t val = 0;
while (load_size--)
val = (val << 8) | packet[offs++];
REG = val;
break;
}
case JMP_OPCODE:
// This can jump backwards. Infinite looping prevented by instructions_remaining.
pc += imm;
break;
case JEQ_OPCODE:
case JNE_OPCODE:
case JGT_OPCODE:
case JLT_OPCODE:
case JSET_OPCODE:
case JNEBS_OPCODE: {
// Load second immediate field.
uint32_t cmp_imm = 0;
if (reg_num == 1) {
cmp_imm = registers[1];
} else if (len_field != 0) {
uint32_t cmp_imm_len = 1 << (len_field - 1);
ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm_len - 1);
uint32_t i;
for (i = 0; i < cmp_imm_len; i++)
cmp_imm = (cmp_imm << 8) | program[pc++];
}
switch (opcode) {
case JEQ_OPCODE:
if (registers[0] == cmp_imm)
pc += imm;
break;
case JNE_OPCODE:
if (registers[0] != cmp_imm)
pc += imm;
break;
case JGT_OPCODE:
if (registers[0] > cmp_imm)
pc += imm;
break;
case JLT_OPCODE:
if (registers[0] < cmp_imm)
pc += imm;
break;
case JSET_OPCODE:
if (registers[0] & cmp_imm)
pc += imm;
break;
case JNEBS_OPCODE: {
// cmp_imm is size in bytes of data to compare.
// pc is offset of program bytes to compare.
// imm is jump target offset.
// REG is offset of packet bytes to compare.
ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1);
ASSERT_IN_PACKET_BOUNDS(REG);
const uint32_t last_packet_offs = REG + cmp_imm - 1;
ASSERT_RETURN(last_packet_offs >= REG);
ASSERT_IN_PACKET_BOUNDS(last_packet_offs);
if (memcmp(program + pc, packet + REG, cmp_imm))
pc += imm;
// skip past comparison bytes
pc += cmp_imm;
break;
}
}
break;
}
case ADD_OPCODE:
registers[0] += reg_num ? registers[1] : imm;
break;
case MUL_OPCODE:
registers[0] *= reg_num ? registers[1] : imm;
break;
case DIV_OPCODE: {
const uint32_t div_operand = reg_num ? registers[1] : imm;
ASSERT_RETURN(div_operand);
registers[0] /= div_operand;
break;
}
case AND_OPCODE:
registers[0] &= reg_num ? registers[1] : imm;
break;
case OR_OPCODE:
registers[0] |= reg_num ? registers[1] : imm;
break;
case SH_OPCODE: {
const int32_t shift_val = reg_num ? (int32_t)registers[1] : signed_imm;
if (shift_val > 0)
registers[0] <<= shift_val;
else
registers[0] >>= -shift_val;
break;
}
case LI_OPCODE:
REG = signed_imm;
break;
case EXT_OPCODE:
if (
// If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result,
// instead just enforce that imm is unsigned (so it's always greater or equal to 0).
#if LDM_EXT_OPCODE == 0
ENFORCE_UNSIGNED(imm) &&
#else
imm >= LDM_EXT_OPCODE &&
#endif
imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) {
REG = memory[imm - LDM_EXT_OPCODE];
} else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) {
memory[imm - STM_EXT_OPCODE] = REG;
} else switch (imm) {
case NOT_EXT_OPCODE:
REG = ~REG;
break;
case NEG_EXT_OPCODE:
REG = -REG;
break;
case SWAP_EXT_OPCODE: {
uint32_t tmp = REG;
REG = OTHER_REG;
OTHER_REG = tmp;
break;
}
case MOV_EXT_OPCODE:
REG = OTHER_REG;
break;
// Unknown extended opcode
default:
// Bail out
return PASS_PACKET;
}
break;
// Unknown opcode
default:
// Bail out
return PASS_PACKET;
}
} while (instructions_remaining--);
return PASS_PACKET;
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2015, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef APF_INTERPRETER_H_
#define APF_INTERPRETER_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Version of APF instruction set processed by accept_packet().
* Should be returned by wifi_get_packet_filter_info.
*/
#define APF_VERSION 2
/**
* Runs a packet filtering program over a packet.
*
* @param program the program bytecode.
* @param program_len the length of {@code apf_program} in bytes.
* @param packet the packet bytes, starting from the 802.3 header and not
* including any CRC bytes at the end.
* @param packet_len the length of {@code packet} in bytes.
* @param filter_age the number of seconds since the filter was programmed.
*
* @return non-zero if packet should be passed to AP, zero if
* packet should be dropped.
*/
int accept_packet(const uint8_t* program, uint32_t program_len,
const uint8_t* packet, uint32_t packet_len,
uint32_t filter_age);
#ifdef __cplusplus
}
#endif
#endif // APF_INTERPRETER_H_

View file

@ -0,0 +1,74 @@
/*
* 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.
*/
// Simple program to try running an APF program against a packet.
#include <libgen.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "apf_interpreter.h"
// Parses hex in "input". Allocates and fills "*output" with parsed bytes.
// Returns length in bytes of "*output".
int parse_hex(char* input, uint8_t** output) {
int length = strlen(input);
if (length & 1) {
fprintf(stderr, "Argument not even number of characters: %s\n", input);
exit(1);
}
length >>= 1;
*output = malloc(length);
if (*output == NULL) {
fprintf(stderr, "Out of memory, tried to allocate %d\n", length);
exit(1);
}
for (int i = 0; i < length; i++) {
char byte[3] = { input[i*2], input[i*2+1], 0 };
char* end_ptr;
(*output)[i] = strtol(byte, &end_ptr, 16);
if (end_ptr != byte + 2) {
fprintf(stderr, "Failed to parse hex %s\n", byte);
exit(1);
}
}
return length;
}
int main(int argc, char* argv[]) {
if (argc != 4) {
fprintf(stderr,
"Usage: %s <program> <packet> <program age>\n"
" program: APF program, in hex\n"
" packet: Packet to run through program, in hex\n"
" program age: Age of program in seconds.\n",
basename(argv[0]));
exit(1);
}
uint8_t* program;
uint32_t program_len = parse_hex(argv[1], &program);
uint8_t* packet;
uint32_t packet_len = parse_hex(argv[2], &packet);
uint32_t filter_age = atoi(argv[3]);
int ret = accept_packet(program, program_len, packet, packet_len,
filter_age);
printf("Packet %sed\n", ret ? "pass" : "dropp");
free(program);
free(packet);
return ret;
}

View file

@ -0,0 +1,16 @@
# Copyright 2017 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.
ifeq ($(BOARD_USES_EASEL),true)
include $(call all-subdir-makefiles)
endif

View file

@ -0,0 +1,126 @@
/*
* Copyright 2017 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 HDR_PLUS_PROFILER_H
#define HDR_PLUS_PROFILER_H
#ifndef ENABLE_HDRPLUS_PROFILER
#define ENABLE_HDRPLUS_PROFILER 0
#endif
#if !ENABLE_HDRPLUS_PROFILER
// If profiler is not enabled, make every macro a noop
#define DECLARE_PROFILER_TIMER(_var, _description)
#define START_PROFILER_TIMER(_var) do {} while(0)
#define END_PROFILER_TIMER(_var) do {} while(0)
#define SCOPE_PROFILER_TIMER(_description) do {} while(0)
#else
#include <string>
/*
* Declare a profiler timer.
*
* _var is the variable that will be declared as a timer.
* _description is the description for this timer. It will be used when logging the timer duration.
*/
#define DECLARE_PROFILER_TIMER(_var, _description) pbcamera::TimerLogger _var = {_description}
/*
* Start a timer.
*
* _var is a timer declared with DECALRE_PROFILER_TIMER.
*/
#define START_PROFILER_TIMER(_var) ((_var).start())
/*
* End a timer and log the duration since last start.
*
* _var is a timer declared with DECALRE_PROFILER_TIMER.
*/
#define END_PROFILER_TIMER(_var) ((_var).end())
/*
* Declare a scope timer that starts now and ends when it goes out of scope.
*
* __description is the description for this timer. It will be used when logging the timer duration.
*/
#define SCOPE_PROFILER_TIMER(_description) pbcamera::ScopeTimerLogger _timer(_description)
namespace pbcamera {
#define TIMER_TAG "[PROFILE_TIMER]"
/**
* TimerLogger provides a timer to log the duration between start() and end().
*/
class TimerLogger {
public:
TimerLogger(const char *name) : mName(name), mInvalid(true) {};
// Start the timer.
void start() {
mInvalid = (clock_gettime(kClockId, &mStartTime) != 0);
}
// End the timer and log the duration since last start.
void end() {
if (mInvalid) {
ALOGE("%s <%s> start time is invalid.", TIMER_TAG, mName.c_str());
return;
}
struct timespec endTime;
mInvalid = (clock_gettime(kClockId, &endTime) != 0);
if (mInvalid) {
ALOGE("%s <%s> end time is invalid.", TIMER_TAG, mName.c_str());
return;
}
int64_t startNs = static_cast<int64_t>(mStartTime.tv_sec) * kNsPerSec + mStartTime.tv_nsec;
int64_t endNs = static_cast<int64_t>(endTime.tv_sec) * kNsPerSec + endTime.tv_nsec;
ALOGI("%s <%s> took %f ms.", TIMER_TAG, mName.c_str(),
static_cast<float>(endNs - startNs) / kNsPerMs);
}
private:
const static int64_t kNsPerSec = 1000000000;
const static int64_t kNsPerMs = 1000000;
const static clockid_t kClockId = CLOCK_BOOTTIME;
std::string mName;
struct timespec mStartTime;
bool mInvalid;
};
/**
* ScopeTimerLogger provides a timer to log the duration of the instance lifetime.
*/
class ScopeTimerLogger {
public:
ScopeTimerLogger(const char *name) : mTimerLogger(name) { mTimerLogger.start(); };
virtual ~ScopeTimerLogger() { mTimerLogger.end(); };
private:
TimerLogger mTimerLogger;
};
} // namespace pbcamera
#endif // !ENABLE_HDRPLUS_PROFILER
#endif // HDR_PLUS_PROFILER_H

View file

@ -0,0 +1,538 @@
/*
* 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.
*/
#ifndef HDR_PLUS_TYPES_H
#define HDR_PLUS_TYPES_H
#include <array>
#include <stdint.h>
#include <string>
#include <vector>
namespace pbcamera {
// This file defines the common types used in HDR+ client and HDR+ service API.
typedef int32_t status_t;
/*
* ImageConfiguration and PlaneConfiguration define the layout of a buffer.
* The following is an example of a NV21 buffer.
*
* <-------Y stride (in bytes)------->
* <----width (in pixels)---->
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . height Y scanline
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . (in lines) (in lines)
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
* Y Y Y Y Y Y Y Y Y Y Y Y Y Y . . . . v |
* . . . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . . . v
* <------V/U stride (in bytes)------>
* V U V U V U V U V U V U V U . . . . ^
* V U V U V U V U V U V U V U . . . . |
* V U V U V U V U V U V U V U . . . . |
* V U V U V U V U V U V U V U . . . . V/U scanline
* V U V U V U V U V U V U V U . . . . (in lines)
* . . . . . . . . . . . . . . . . . . |
* . . . . . . . . . . . . . . . . . . v
* . . . . . . . . . . . . . . . . . . -> Image padding.
*/
// PlaneConfiguration defines an image planes configuration.
struct PlaneConfiguration {
// Number of bytes in each line including padding.
uint32_t stride;
// Number of lines vertically including padding.
uint32_t scanline;
PlaneConfiguration() : stride(0), scanline(0) {};
bool operator==(const PlaneConfiguration &other) const {
return stride == other.stride &&
scanline == other.scanline;
}
bool operator!=(const PlaneConfiguration &other) const {
return !(*this == other);
}
};
// ImageConfiguration defines an image configuration.
struct ImageConfiguration {
// Image width.
uint32_t width;
// Image height;
uint32_t height;
// Image format;
int format;
// Configuration for each planes.
std::vector<PlaneConfiguration> planes;
// Number of padded bytes after the last plane.
uint32_t padding;
ImageConfiguration() : width(0), height(0), format(0), padding(0) {};
bool operator==(const ImageConfiguration &other) const {
return width == other.width &&
height == other.height &&
format == other.format &&
planes == other.planes &&
padding == other.padding;
}
bool operator!=(const ImageConfiguration &other) const {
return !(*this == other);
}
};
/*
* StreamConfiguration defines a stream's configuration, such as its image buffer resolution, used
* during stream configuration.
*/
struct StreamConfiguration {
/*
* Unique ID of the stream. Each stream must have an unique ID so it can be used to identify
* the output streams of a StreamBuffer in CaptureRequest.
*/
uint32_t id;
// Image configuration.
ImageConfiguration image;
bool operator==(const StreamConfiguration &other) const {
return id == other.id &&
image == other.image;
}
bool operator!=(const StreamConfiguration &other) const {
return !(*this == other);
}
};
/*
* SensorMode contains the sensor mode information.
*/
struct SensorMode {
// Usually 0 is back camera and 1 is front camera.
uint32_t cameraId;
// Pixel array resolution.
uint32_t pixelArrayWidth;
uint32_t pixelArrayHeight;
// Active array resolution.
uint32_t activeArrayWidth;
uint32_t activeArrayHeight;
// Sensor output pixel clock.
uint32_t outputPixelClkHz;
// Sensor timestamp offset due to gyro calibration. When comparing timestamps between AP and
// Easel, this offset should be subtracted from AP timestamp.
int64_t timestampOffsetNs;
// Sensor timestamp offset due to sensor cropping. When comparing timestamps between AP and
// Easel, this offset should be subtracted from AP timestamp.
int64_t timestampCropOffsetNs;
// Sensor output format as defined in android_pixel_format.
int format;
SensorMode() : cameraId(0), pixelArrayWidth(0), pixelArrayHeight(0), activeArrayWidth(0),
activeArrayHeight(0), outputPixelClkHz(0) {};
};
/*
* InputConfiguration defines the input configuration for HDR+ service.
*/
struct InputConfiguration {
// Whether the input frames come from sensor MIPI or AP. If true, HDR+ service will get input
// frames from sensor and sensorMode contains the sensor mode information. If false, HDR+
// service will get input frames from AP and streamConfig contains the input stream
// configuration.
bool isSensorInput;
// Sensor mode if isSensorInput is true.
SensorMode sensorMode;
// Input stream configuration if isSensorInput is false.
StreamConfiguration streamConfig;
InputConfiguration() : isSensorInput(false) {};
};
/*
* StreamBuffer defines a buffer in a stream.
*/
struct StreamBuffer {
// ID of the stream that this buffer belongs to.
uint32_t streamId;
// DMA buffer fd for this buffer if it's an ION buffer.
int32_t dmaBufFd;
// Pointer to the data of this buffer.
void* data;
// Size of the allocated data.
uint32_t dataSize;
};
/*
* CaptureRequest defines a capture request that HDR+ client sends to HDR+ service.
*/
struct CaptureRequest {
/*
* ID of the capture request. Each capture request must have an unique ID. When HDR+ service
* sends a CaptureResult to HDR+ client for this request, CaptureResult.requestId will be
* assigned to this ID.
*/
uint32_t id;
/*
* Output buffers of the request. The buffers will be filled with captured image when HDR+
* service sends the output buffers in CaptureResult.
*/
std::vector<StreamBuffer> outputBuffers;
};
// Util functions used in StaticMetadata and FrameMetadata.
namespace metadatautils {
template<typename T>
void appendValueToString(std::string *strOut, const char* key, T value);
template<typename T>
void appendVectorOrArrayToString(std::string *strOut, T values);
template<typename T>
void appendVectorOrArrayToString(std::string *strOut, const char* key, T values);
template<typename T, size_t SIZE>
void appendVectorArrayToString(std::string *strOut, const char* key,
std::vector<std::array<T, SIZE>> values);
template<typename T, size_t SIZE>
void appendArrayArrayToString(std::string *strOut, const char* key,
std::array<T, SIZE> values);
} // namespace metadatautils
static const uint32_t DEBUG_PARAM_NONE = 0u;
static const uint32_t DEBUG_PARAM_SAVE_GCAME_INPUT_METERING = (1u);
static const uint32_t DEBUG_PARAM_SAVE_GCAME_INPUT_PAYLOAD = (1u << 1);
static const uint32_t DEBUG_PARAM_SAVE_GCAME_TEXT = (1u << 2);
/*
* StaticMetadata defines a camera device's characteristics.
*
* If this structure is changed, serialization in MessengerToHdrPlusService and deserialization in
* MessengerListenerFromHdrPlusClient should also be updated.
*/
struct StaticMetadata {
// The following are from Android Camera Metadata
uint8_t flashInfoAvailable; // android.flash.info.available
std::array<int32_t, 2> sensitivityRange; // android.sensor.info.sensitivityRange
int32_t maxAnalogSensitivity; // android.sensor.maxAnalogSensitivity
std::array<int32_t, 2> pixelArraySize; // android.sensor.info.pixelArraySize
std::array<int32_t, 4> activeArraySize; // android.sensor.info.activeArraySize
std::vector<std::array<int32_t, 4>> opticalBlackRegions; // android.sensor.opticalBlackRegions
// android.scaler.availableStreamConfigurations
std::vector<std::array<int32_t, 4>> availableStreamConfigurations;
uint8_t referenceIlluminant1; // android.sensor.referenceIlluminant1
uint8_t referenceIlluminant2; // android.sensor.referenceIlluminant2
std::array<float, 9> calibrationTransform1; // android.sensor.calibrationTransform1
std::array<float, 9> calibrationTransform2; // android.sensor.calibrationTransform2
std::array<float, 9> colorTransform1; // android.sensor.colorTransform1
std::array<float, 9> colorTransform2; // android.sensor.colorTransform2
int32_t whiteLevel; // android.sensor.info.whiteLevel
uint8_t colorFilterArrangement; // android.sensor.info.colorFilterArrangement
std::vector<float> availableApertures; // android.lens.info.availableApertures
std::vector<float> availableFocalLengths; // android.lens.info.availableFocalLengths
std::array<int32_t, 2> shadingMapSize; // android.lens.info.shadingMapSize
uint8_t focusDistanceCalibration; // android.lens.info.focusDistanceCalibration
std::array<int32_t, 2> aeCompensationRange; // android.control.aeCompensationRange
float aeCompensationStep; // android.control.aeCompensationStep
uint32_t debugParams; // Use HDRPLUS_DEBUG_PARAM_*
// Convert this static metadata to a string and append it to the specified string.
void appendToString(std::string *strOut) const {
if (strOut == nullptr) return;
metadatautils::appendValueToString(strOut, "flashInfoAvailable", flashInfoAvailable);
metadatautils::appendVectorOrArrayToString(strOut, "sensitivityRange", sensitivityRange);
metadatautils::appendValueToString(strOut, "maxAnalogSensitivity", maxAnalogSensitivity);
metadatautils::appendVectorOrArrayToString(strOut, "pixelArraySize", pixelArraySize);
metadatautils::appendVectorOrArrayToString(strOut, "activeArraySize", activeArraySize);
metadatautils::appendVectorArrayToString(strOut, "opticalBlackRegions",
opticalBlackRegions);
metadatautils::appendVectorArrayToString(strOut, "availableStreamConfigurations",
availableStreamConfigurations);
metadatautils::appendValueToString(strOut, "referenceIlluminant1", referenceIlluminant1);
metadatautils::appendValueToString(strOut, "referenceIlluminant2", referenceIlluminant2);
metadatautils::appendVectorOrArrayToString(strOut, "calibrationTransform1",
calibrationTransform1);
metadatautils::appendVectorOrArrayToString(strOut, "calibrationTransform2",
calibrationTransform2);
metadatautils::appendVectorOrArrayToString(strOut, "colorTransform1", colorTransform1);
metadatautils::appendVectorOrArrayToString(strOut, "colorTransform2", colorTransform2);
metadatautils::appendValueToString(strOut, "whiteLevel", whiteLevel);
metadatautils::appendValueToString(strOut, "colorFilterArrangement",
colorFilterArrangement);
metadatautils::appendVectorOrArrayToString(strOut, "availableApertures",
availableApertures);
metadatautils::appendVectorOrArrayToString(strOut, "availableFocalLengths",
availableFocalLengths);
metadatautils::appendVectorOrArrayToString(strOut, "shadingMapSize", shadingMapSize);
metadatautils::appendValueToString(strOut, "focusDistanceCalibration",
focusDistanceCalibration);
metadatautils::appendVectorOrArrayToString(strOut, "aeCompensationRange",
aeCompensationRange);
metadatautils::appendValueToString(strOut, "aeCompensationStep",
aeCompensationStep);
metadatautils::appendValueToString(strOut, "debugParams", debugParams);
}
};
/*
* FrameMetadata defines properties of a frame captured on AP.
*
* If this structure is changed, serialization in MessengerToHdrPlusService and deserialization in
* MessengerListenerFromHdrPlusClient should also be updated.
*/
struct FrameMetadata {
int64_t easelTimestamp; // Easel timestamp
// The following are from Android Camera Metadata
int64_t exposureTime; // android.sensor.exposureTime
int32_t sensitivity; // android.sensor.sensitivity
int32_t postRawSensitivityBoost; // android.control.postRawSensitivityBoost
uint8_t flashMode; // android.flash.mode
std::array<float, 4> colorCorrectionGains; // android.colorCorrection.gains
std::array<float, 9> colorCorrectionTransform; // android.colorCorrection.transform
std::array<float, 3> neutralColorPoint; // android.sensor.neutralColorPoint
int64_t timestamp; // android.sensor.timestamp
uint8_t blackLevelLock; // android.blackLevel.lock
uint8_t faceDetectMode; // android.statistics.faceDetectMode
std::vector<int32_t> faceIds; // android.statistics.faceIds
std::vector<std::array<int32_t, 6>> faceLandmarks; // android.statistics.faceLandmarks
std::vector<std::array<int32_t, 4>> faceRectangles; // android.statistics.faceRectangles
std::vector<uint8_t> faceScores; // android.statistics.faceScores
uint8_t sceneFlicker; // android.statistics.sceneFlicker
std::array<std::array<double, 2>, 4> noiseProfile; // android.sensor.noiseProfile
std::array<float, 4> dynamicBlackLevel; // android.sensor.dynamicBlackLevel
std::vector<float> lensShadingMap; // android.statistics.lensShadingMap
float focusDistance; // android.lens.focusDistance
int32_t aeExposureCompensation; // android.control.aeExposureCompensation
uint8_t aeMode; // android.control.aeMode
uint8_t aeLock; // android.control.aeLock
uint8_t aeState; // android.control.aeState
uint8_t aePrecaptureTrigger; // android.control.aePrecaptureTrigger
std::vector<std::array<int32_t, 5>> aeRegions; // android.control.aeRegions
// Convert this static metadata to a string and append it to the specified string.
void appendToString(std::string *strOut) const {
if (strOut == nullptr) return;
metadatautils::appendValueToString(strOut, "easelTimestamp", easelTimestamp);
metadatautils::appendValueToString(strOut, "exposureTime", exposureTime);
metadatautils::appendValueToString(strOut, "sensitivity", sensitivity);
metadatautils::appendValueToString(strOut, "postRawSensitivityBoost",
postRawSensitivityBoost);
metadatautils::appendValueToString(strOut, "flashMode", flashMode);
metadatautils::appendVectorOrArrayToString(strOut, "colorCorrectionGains",
colorCorrectionGains);
metadatautils::appendVectorOrArrayToString(strOut, "colorCorrectionTransform",
colorCorrectionTransform);
metadatautils::appendVectorOrArrayToString(strOut, "neutralColorPoint", neutralColorPoint);
metadatautils::appendValueToString(strOut, "timestamp", timestamp);
metadatautils::appendValueToString(strOut, "blackLevelLock", blackLevelLock);
metadatautils::appendValueToString(strOut, "faceDetectMode", faceDetectMode);
metadatautils::appendVectorOrArrayToString(strOut, "faceIds", faceIds);
metadatautils::appendVectorArrayToString(strOut, "faceLandmarks", faceLandmarks);
metadatautils::appendVectorArrayToString(strOut, "faceRectangles", faceRectangles);
metadatautils::appendVectorOrArrayToString(strOut, "faceScores", faceScores);
metadatautils::appendArrayArrayToString(strOut, "noiseProfile", noiseProfile);
metadatautils::appendValueToString(strOut, "sceneFlicker", sceneFlicker);
metadatautils::appendVectorOrArrayToString(strOut, "dynamicBlackLevel", dynamicBlackLevel);
metadatautils::appendVectorOrArrayToString(strOut, "lensShadingMap", lensShadingMap);
metadatautils::appendValueToString(strOut, "focusDistance", focusDistance);
metadatautils::appendValueToString(strOut, "aeExposureCompensation", aeExposureCompensation);
metadatautils::appendValueToString(strOut, "aeMode", aeMode);
metadatautils::appendValueToString(strOut, "aeLock", aeLock);
metadatautils::appendValueToString(strOut, "aeState", aeState);
metadatautils::appendValueToString(strOut, "aePrecaptureTrigger", aePrecaptureTrigger);
metadatautils::appendVectorArrayToString(strOut, "aeRegions", aeRegions);
}
};
/*
* RequestMetadata defines the properties for a capture request.
*
* If this structure is changed, serialization in MessengerToHdrPlusClient and deserialization in
* MessengerListenerFromHdrPlusService should also be updated.
*/
struct RequestMetadata {
std::array<int32_t, 4> cropRegion; // android.scaler.cropRegion (x_min, y_min, width, height)
int32_t aeExposureCompensation; // android.control.aeExposureCompensation
bool postviewEnable; // com.google.nexus.experimental2017.stats.postview_enable
bool continuousCapturing; // Whether to capture RAW while HDR+ processing.
// Convert this static metadata to a string and append it to the specified string.
void appendToString(std::string *strOut) const {
if (strOut == nullptr) return;
metadatautils::appendVectorOrArrayToString(strOut, "cropRegion", cropRegion);
metadatautils::appendValueToString(strOut, "aeExposureCompensation", aeExposureCompensation);
metadatautils::appendValueToString(strOut, "postviewEnable", postviewEnable);
metadatautils::appendValueToString(strOut, "continuousCapturing", continuousCapturing);
}
};
/*
* ResultMetadata defines a process frame's properties that have been modified due to processing.
*
* If this structure is changed, serialization in MessengerToHdrPlusClient and deserialization in
* MessengerListenerFromHdrPlusService should also be updated.
*/
struct ResultMetadata {
int64_t easelTimestamp; // Easel timestamp of SOF of the base frame.
int64_t timestamp; // android.sensor.timestamp. AP timestamp of exposure start of the base
// frame.
std::string makernote; // Obfuscated capture information.
// Convert this static metadata to a string and append it to the specified string.
void appendToString(std::string *strOut) const {
if (strOut == nullptr) return;
metadatautils::appendValueToString(strOut, "easelTimestamp", easelTimestamp);
metadatautils::appendValueToString(strOut, "timestamp", timestamp);
metadatautils::appendValueToString(strOut, "makernote", makernote.size());
}
};
/*
* CaptureResult defines a capture result that HDR+ service returns to HDR+ client.
*/
struct CaptureResult {
/*
* ID of the CaptureRequest that this capture result corresponds to. It can be used to match
* the original CaptureRequest when the HDR+ client receives this result.
*/
uint32_t requestId;
/*
* Output buffers filled with processed frame by HDR+ service.
*/
std::vector<StreamBuffer> outputBuffers;
/*
* Result metadata including modified properties due to processing.
*/
ResultMetadata metadata;
};
// Util functions used in StaticMetadata and FrameMetadata.
namespace metadatautils {
/*
* Append a key and a value to a string.
*
* strOut is the string to append a key and a value to.
* key is the name of the data.
* value is the value of the data.
*/
template<typename T>
void appendValueToString(std::string *strOut, const char* key, T value) {
if (strOut == nullptr) return;
(*strOut) += std::string(key) + ": " + std::to_string(value) + "\n";
}
/*
* Append a vector or an array of values to a string.
*
* strOut is the string to append a key and values to.
* values is a vector or an array containing values to append to the string.
*/
template<typename T>
void appendVectorOrArrayToString(std::string *strOut, T values) {
if (strOut == nullptr) return;
for (size_t i = 0; i < values.size(); i++) {
(*strOut) += std::to_string(values[i]);
if (i != values.size() - 1) {
(*strOut) +=", ";
}
}
}
/*
* Append a key and a vector or an array of values to a string.
*
* strOut is the string to append a key and values to.
* key is the name of the data.
* values is a vector or an array containing values to append to the string.
*/
template<typename T>
void appendVectorOrArrayToString(std::string *strOut, const char* key, T values) {
if (strOut == nullptr) return;
(*strOut) += std::string(key) + ": ";
appendVectorOrArrayToString(strOut, values);
(*strOut) += "\n";
}
/*
* Append a key and a vector of arrays to a string.
*
* strOut is the string to append a key and values to.
* key is the name of the data.
* values is a vector of arrays containing values to append to the string.
*/
template<typename T, size_t SIZE>
void appendVectorArrayToString(std::string *strOut, const char* key,
std::vector<std::array<T, SIZE>> values) {
if (strOut == nullptr) return;
(*strOut) += std::string(key) + ": ";
for (size_t i = 0; i < values.size(); i++) {
appendVectorOrArrayToString(strOut, values[i]);
if (i != values.size() - 1) {
(*strOut) +=", ";
}
}
(*strOut) += "\n";
}
/*
* Append a key and an array of arrays to a string.
*
* strOut is the string to append a key and values to.
* key is the name of the data.
* values is an array of arrays containing values to append to the string.
*/
template<typename T, size_t SIZE>
void appendArrayArrayToString(std::string *strOut, const char* key,
std::array<T, SIZE> values) {
if (strOut == nullptr) return;
(*strOut) += std::string(key) + ": ";
for (size_t i = 0; i < values.size(); i++) {
appendVectorOrArrayToString(strOut, values[i]);
if (i != values.size() - 1) {
(*strOut) +=", ";
}
}
(*strOut) += "\n";
}
} // namespace metadatautils
} // namespace pbcamera
#endif // HDR_PLUS_TYPES_H

View file

@ -0,0 +1,56 @@
# 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)
#
# libhdrplusclient
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
EaselManagerClient.cpp \
HdrPlusClientUtils.cpp
LOCAL_SHARED_LIBRARIES := liblog
# For AOSP builds, use dummy implementation.
ifeq ($(wildcard vendor/google_easel),)
LOCAL_CFLAGS += -DUSE_DUMMY_IMPL=1
else
LOCAL_CFLAGS += -DUSE_DUMMY_IMPL=0
LOCAL_SHARED_LIBRARIES += libhdrplusclientimpl
endif
LOCAL_HEADER_LIBRARIES := \
libsystem_headers \
libutils_headers
LOCAL_EXPORT_HEADER_LIBRARY_HEADERS := \
libutils_headers
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/include \
hardware/google/easel/camera/include
LOCAL_CFLAGS += -Wall -Wextra -Werror
LOCAL_EXPORT_C_INCLUDE_DIRS += \
$(LOCAL_PATH)/include \
hardware/google/easel/camera/include
LOCAL_MODULE:= libhdrplusclient
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_OWNER := google
include $(BUILD_SHARED_LIBRARY)

View file

@ -0,0 +1,37 @@
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "EaselManagerClient"
#include <log/log.h>
#include <memory>
#include "EaselManagerClient.h"
#if !USE_DUMMY_IMPL
#include "EaselManagerClientImpl.h"
#endif
namespace android {
std::unique_ptr<EaselManagerClient> EaselManagerClient::create() {
#if USE_DUMMY_IMPL
return nullptr;
#else
return std::make_unique<EaselManagerClientImpl>();
#endif
}
} // namespace android

View file

@ -0,0 +1,197 @@
/*
* 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.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "HdrPlusClientUtils"
#include <log/log.h>
#include <fstream>
#include <inttypes.h>
#include <system/graphics.h>
#include "HdrPlusClientUtils.h"
namespace android {
namespace hdrplus_client_utils {
// Get the RGB values of the pixel at (x, y).
static status_t getRgb(uint8_t *r, uint8_t *g, uint8_t* b, uint32_t x, uint32_t y,
const pbcamera::StreamConfiguration &streamConfig,
const pbcamera::StreamBuffer &buffer) {
switch (streamConfig.image.format) {
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
{
// Check the stream configuration has two planes.
if (streamConfig.image.planes.size() != 2) {
ALOGE("%s: NV21 should have 2 planes but it has %zu", __FUNCTION__,
streamConfig.image.planes.size());
return BAD_VALUE;
}
// Find the indices of Y, V, and U in the buffer.
uint32_t yIndex = y * streamConfig.image.planes[0].stride + x;
uint32_t vIndex = streamConfig.image.planes[0].scanline *
streamConfig.image.planes[0].stride +
(y / 2) * streamConfig.image.planes[1].stride + (x & ~0x1);
uint32_t uIndex = vIndex + 1;
// Convert YUV to RGB.
int32_t yc = ((uint8_t*)buffer.data)[yIndex];
int32_t vc = ((uint8_t*)buffer.data)[vIndex] - 128;
int32_t uc = ((uint8_t*)buffer.data)[uIndex] - 128;
*r = std::min(std::max(yc + 0.003036f * uc + 1.399457f * vc, 0.0f), 255.0f);
*g = std::min(std::max(yc - 0.344228f * uc - 0.717202f * vc, 0.0f), 255.0f);
*b = std::min(std::max(yc + 1.772431f * uc - 0.006137f * vc, 0.0f), 255.0f);
return OK;
}
case HAL_PIXEL_FORMAT_RGB_888:
{
// Check the stream configuration has 1 plane.
if (streamConfig.image.planes.size() != 1) {
ALOGE("%s: RGB_888 should have 1 plane but it has %zu", __FUNCTION__,
streamConfig.image.planes.size());
return BAD_VALUE;
}
uint32_t offset = y * streamConfig.image.planes[0].stride + x * 3;
*r = ((uint8_t*)buffer.data)[offset];
*g = ((uint8_t*)buffer.data)[offset + 1];
*b = ((uint8_t*)buffer.data)[offset + 2];
return OK;
}
default:
ALOGE("%s: Format %d is not supported.", __FUNCTION__, streamConfig.image.format);
return BAD_VALUE;
}
}
status_t writePpm(const std::string &filename, const pbcamera::StreamConfiguration &streamConfig,
const pbcamera::StreamBuffer &buffer) {
if (streamConfig.image.format != HAL_PIXEL_FORMAT_YCrCb_420_SP &&
streamConfig.image.format != HAL_PIXEL_FORMAT_RGB_888) {
ALOGE("%s: format 0x%x is not supported.", __FUNCTION__, streamConfig.image.format);
return BAD_VALUE;
}
std::ofstream outfile(filename, std::ios::binary);
if (!outfile.is_open()) {
ALOGE("%s: Opening file (%s) failed.", __FUNCTION__, filename.data());
return NO_INIT;
}
uint32_t width = streamConfig.image.width;
uint32_t height = streamConfig.image.height;
// Write headers of the ppm file.
outfile << "P6";
outfile << " " << std::to_string(width) << " " << std::to_string(height) << " 255 ";
// Write RGB values of the image.
uint8_t r, g, b;
for (uint32_t y = 0; y < height; y++) {
for (uint32_t x = 0; x < width; x++) {
status_t res = getRgb(&r, &g, &b, x, y, streamConfig, buffer);
if (res != OK) {
ALOGE("%s: Getting RGB failed: %s (%d).", __FUNCTION__, strerror(-res), res);
return res;
}
outfile << r << g << b;
}
}
ALOGD("%s: Saved file: %s", __FUNCTION__, filename.data());
outfile.close();
return OK;
}
status_t comparePpm(const std::string &filename, const pbcamera::StreamConfiguration &streamConfig,
const pbcamera::StreamBuffer &buffer, float *diffRatio) {
if (streamConfig.image.format != HAL_PIXEL_FORMAT_YCrCb_420_SP) {
ALOGE("%s: format 0x%x is not supported.", __FUNCTION__, streamConfig.image.format);
return BAD_VALUE;
}
std::ifstream ifile(filename, std::ios::binary);
if (!ifile.is_open()) {
ALOGE("%s: Opening file (%s) failed.", __FUNCTION__, filename.data());
return NO_INIT;
}
std::string s;
// Read headers of the ppm file.
ifile >> s;
if (s != "P6") {
ALOGE("%s: Invalid PPM file header: %s", __FUNCTION__, s.c_str());
return BAD_VALUE;
}
// Read width and height.
ifile >> s;
uint32_t width = std::stoul(s);
ifile >> s;
uint32_t height = std::stoul(s);
if (width != streamConfig.image.width || height != streamConfig.image.height) {
ALOGE("%s: Image resolution doesn't match. image %dx%d ppm %dx%d",
__FUNCTION__, streamConfig.image.width, streamConfig.image.height,
width, height);
return BAD_VALUE;
}
ifile >> s;
if (s != "255") {
ALOGE("%s: Expecting 255 but got %s", __FUNCTION__, s.c_str());
return BAD_VALUE;
}
char c;
// Get a space
ifile.get(c);
// Now the RGB values start.
uint8_t r, g, b;
uint64_t diff = 0;
for (uint32_t y = 0; y < height; y++) {
for (uint32_t x = 0; x < width; x++) {
status_t res = getRgb(&r, &g, &b, x, y, streamConfig, buffer);
if (res != OK) {
ALOGE("%s: Getting RGB failed: %s (%d).", __FUNCTION__, strerror(-res), res);
return res;
}
// Get r, g, b from golden image and accumulate the differences.
ifile.get(c);
diff += abs(static_cast<int32_t>(c) - r);
ifile.get(c);
diff += abs(static_cast<int32_t>(c) - g);
ifile.get(c);
diff += abs(static_cast<int32_t>(c) - b);
}
}
if (diffRatio != nullptr) {
*diffRatio = diff / (static_cast<float>(width) * height * 3 * 256);
}
return OK;
}
} // hdrplus_client_utils
} // namespace android

View file

@ -0,0 +1,155 @@
/*
* Copyright 2017 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 EASEL_MANAGER_CLIENT_H
#define EASEL_MANAGER_CLIENT_H
#include <future>
#include <utils/Errors.h>
#include <utils/Mutex.h>
#define FW_VER_SIZE 24
namespace android {
class EaselManagerClientListener;
class HdrPlusClient;
class HdrPlusClientListener;
class EaselManagerClient {
public:
static std::unique_ptr<EaselManagerClient> create();
EaselManagerClient() {};
virtual ~EaselManagerClient() {};
/*
* Return if Easel is present on the device.
*
* If Easel is not present, all other calls to HdrPlusClient are invalid.
*/
virtual bool isEaselPresentOnDevice() const = 0;
/*
* Open Easel manager client.
*
* This will power on Easel and initialize Easel manager client.
*/
virtual status_t open() = 0;
/*
* Suspend Easel.
*
* Put Easel on suspend mode.
*/
virtual status_t suspend() = 0;
/*
* Resume Easel.
*
* Resume Easel from suspend mode.
*
* listener will be invoked for Easel status.
*/
virtual status_t resume(EaselManagerClientListener *listener) = 0;
/*
* Retrieve Easel firmware version.
*
* Firmware version string is added to image exif
*/
virtual status_t getFwVersion(char *fwVersion) = 0;
/*
* Start MIPI with an output pixel lock rate for a camera.
*
* Can be called when Easel is powered on or resumed, for Easel to start sending sensor data
* to AP.
*
* cameraId is the camera ID to start MIPI for.
* outputPixelClkHz is the output pixel rate.
* enableCapture is whether to enable MIPI capture on Easel.
*/
virtual status_t startMipi(uint32_t cameraId, uint32_t outputPixelClkHz,
bool enableCapture) = 0;
/*
* Stop MIPI for a camera.
*
* cameraId is the camera is ID to stop MIPI for.
*/
virtual status_t stopMipi(uint32_t cameraId) = 0;
/*
* Open an HDR+ client asynchronously.
*
* Open an HDR+ client asynchronouly. When an HDR+ client is opened,
* HdrPlusClientListener::onOpened() will be invoked with the created HDR+ client. If opening
* an HDR+ client failed, HdrPlusClientListener::onOpenFailed() will be invoked with the error.
* If this method returns an error, HdrPlusClientListener::onOpenFailed() will not be invoked.
*
* listener is an HDR+ client listener.
*
* Returns:
* OK: if initiating opening an HDR+ client asynchronously was successful.
* HdrPlusClientListener::onOpened() or HdrPlusClientListener::onOpenFailed()
* will be invoked when opening an HDR+ client completed.
* ALREADY_EXISTS: if there is already a pending HDR+ client being opened.
*/
virtual status_t openHdrPlusClientAsync(HdrPlusClientListener *listener) = 0;
/*
* Open an HDR+ client synchronously and block until it completes.
*
* listener is an HDR+ client listener.
* client is an output parameter for created HDR+ client.
*
* Returns:
* OK: on success.
* -EEXIST: if an HDR+ client is already opened.
* -ENODEV: if opening an HDR+ failed due to a serious error.
*/
virtual status_t openHdrPlusClient(HdrPlusClientListener *listener,
std::unique_ptr<HdrPlusClient> *client) = 0;
/*
* Close an HDR+ client.
*
* client is the HDR+ client to be closed.
*/
virtual void closeHdrPlusClient(std::unique_ptr<HdrPlusClient> client) = 0;
private:
// Disallow copy and assign.
EaselManagerClient(const EaselManagerClient&) = delete;
void operator=(const EaselManagerClient&) = delete;
};
/*
* EaselManagerClientListener defines callbacks that will be invoked by EaselManagerClient.
*/
class EaselManagerClientListener {
public:
virtual ~EaselManagerClientListener() {};
// Invoked when Easel encountered a fatal error. Client should shut down Easel.
virtual void onEaselFatalError(std::string errMsg) = 0;
};
} // namespace android
#endif // EASEL_MANAGER_CLIENT_H

View file

@ -0,0 +1,160 @@
/*
* 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.
*/
#ifndef HDR_PLUS_CLIENT_H
#define HDR_PLUS_CLIENT_H
#include "CameraMetadata.h"
#include "hardware/camera3.h"
#include "HdrPlusClientListener.h"
#include "HdrPlusTypes.h"
using ::android::hardware::camera::common::V1_0::helper::CameraMetadata;
namespace android {
/**
* HdrPlusClient
*
* HdrPlusClient class can be used to connect to HDR+ service to perform HDR+ processing on
* Easel.
*/
class HdrPlusClient {
public:
// HdrPlusClientListener is the listener to receive callbacks from HDR+ client. The listener
// must be valid during the life cycle of HdrPlusClient
HdrPlusClient(HdrPlusClientListener *) {};
/*
* The recommended way to create an HdrPlusClient instance is via
* EaselManagerClient::openHdrPlusClientAsync() or EaselManagerClient::openHdrPlusClientAsync().
* EaselManagerClient will make sure Easel is in a valid state to open an HDR+ client. To close
* an HdrPlusClient, use EaselManagerClient::closeHdrPlusClient.
*/
virtual ~HdrPlusClient() {};
/*
* Connect to HDR+ service.
*
* If EaselManagerClient is used to create the HdrPlusClient, it is already connected.
*
* Returns:
* 0: on success.
* -EEXIST: if it's already connected.
* -ENODEV: if connecting failed due to a serious error.
*/
virtual status_t connect() = 0;
/*
* Set the static metadata of current camera device.
*
* Must be called after connect() and before configuring streams.
*
* staticMetadata is the static metadata of current camera device.
*
* Returns:
* 0: on success.
* -ENODEV: if HDR+ service is not connected.
*/
virtual status_t setStaticMetadata(const camera_metadata_t &staticMetadata) = 0;
/*
* Configure streams.
*
* Must be called when configuration changes including input (sensor) resolution and format, and
* output resolutions and formats.
*
* inputConfig contains the information about the input frames or sensor configurations.
* outputConfigs is a vector of output stream configurations.
*
* Returns:
* 0: on success.
* -EINVAL: if outputConfigs is empty or the configurations are not supported.
* -ENODEV: if HDR+ service is not connected.
*/
virtual status_t configureStreams(const pbcamera::InputConfiguration &inputConfig,
const std::vector<pbcamera::StreamConfiguration> &outputConfigs) = 0;
/*
* Enable or disable ZSL HDR+ mode.
*
* When ZSL HDR+ mode is enabled, Easel will capture ZSL RAW buffers. ZSL HDR+ mode should be
* disabled to reduce power consumption when HDR+ processing is not necessary, e.g in video
* mode.
*
* enabled is a flag indicating whether to enable ZSL HDR+ mode.
*
* Returns:
* 0: on success.
* -ENODEV: if HDR+ service is not connected, or streams are not configured.
*/
virtual status_t setZslHdrPlusMode(bool enabled) = 0;
/*
* Submit a capture request for HDR+ outputs.
*
* For each output buffer in CaptureRequest, it will be returned in a CaptureResult via
* HdrPlusClientListener::onCaptureResult(). HdrPlusClientListener::onCaptureResult() may be
* invoked multiple times to return all output buffers in one CaptureRequest. Each output
* buffer will be returned in CaptureResult only once.
*
* request is a CaptureRequest containing output buffers to be filled by HDR+ service.
* requestMetadata is the metadata for this request.
*
* Returns:
* 0: on success.
* -EINVAL: if the request is invalid such as containing invalid stream IDs.
*/
virtual status_t submitCaptureRequest(pbcamera::CaptureRequest *request,
const CameraMetadata &requestMetadata) = 0;
/*
* Send an input buffer to HDR+ service. This is used when HDR+ service's input buffers come
* from the client rather than MIPI.
*
* inputBuffer is the input buffer to send to HDR+ service. After this method returns, the
* buffer has been copied (with DMA) to HDR+ service and the caller has the
* ownership of the buffer.
*/
virtual void notifyInputBuffer(const pbcamera::StreamBuffer &inputBuffer,
int64_t timestampNs) = 0;
/*
* Notify about result metadata of a frame that AP captured. This may be called multiple times
* for a frame to send multiple partial metadata and lastMetadata must be false except for the
* last partial metadata. When there is only one metadata for a frame, lastMetadata must be
* true.
*
* frameNumber is a unique frame number that the metadata belong to.
* resultMetadata is the result metadata of a frame that AP captured.
* lastMetadata is a flag indicating whether this is the last metadata for the frame number.
*/
virtual void notifyFrameMetadata(uint32_t frameNumber, const camera_metadata_t &resultMetadata,
bool lastMetadata=true) = 0;
/*
* Notify Easel has encountered a fatal error and HDR+ client should stop sending messages
* to Easel.
*/
virtual void nofityEaselFatalError() = 0;
private:
// Disallow copy and assign.
HdrPlusClient(const HdrPlusClient&) = delete;
void operator=(const HdrPlusClient&) = delete;
};
} // namespace android
#endif // HDR_PLUS_CLIENT_H

View file

@ -0,0 +1,93 @@
/*
* 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.
*/
#ifndef HDR_PLUS_CLIENT_LISTENER_H
#define HDR_PLUS_CLIENT_LISTENER_H
#include "hardware/camera3.h"
#include "HdrPlusTypes.h"
namespace android {
class HdrPlusClient;
/*
* HdrPlusClientListener defines callbacks that will be invoked by HdrPlusClient for events like
* returning capture results.
*/
class HdrPlusClientListener {
public:
virtual ~HdrPlusClientListener() {};
/*
* Invoked when an HDR+ client is opened successfully via
* EaselManagerClient::openHdrPlusClientAsync.
*/
virtual void onOpened(std::unique_ptr<HdrPlusClient> client) = 0;
/*
* Invoked when opening an HDR+ client failed via EaselManagerClient::openHdrPlusClientAsync.
*
* err is
* -EEXIST: if an HDR+ client is already opened.
* -ENODEV: if opening an HDR+ failed due to a serious error.
*/
virtual void onOpenFailed(status_t err) = 0;
/*
* Invoked when HDR+ client is in a fatal error state. After receiving this error, calls to HDR+
* client will not succeed and HDR+ client should be closed.
*/
virtual void onFatalError() = 0;
/*
* Invoked when a CaptureResult, containing a subset or all output buffers for a CaptureRequest,
* is received. This may be invoked multiple times for one CaptureRequest but each CaptureResult
* will contain distinct output buffers that have not been received yet.
*/
virtual void onCaptureResult(pbcamera::CaptureResult *result,
const camera_metadata_t &resultMetadata) = 0;
/*
* Invoked when a failed CaptureResult, containing a subset or all output buffers for a
* CaptureRequest, is received. Output buffers in a failed capture result may contain garbage
* data. This may be invoked multiple times for one CaptureRequest but each CaptureResult
* will contain distinct output buffers that have not been received yet.
*/
virtual void onFailedCaptureResult(pbcamera::CaptureResult *failedResult) = 0;
/*
* Invoked when HDR+ processing has started for a request. requestId is the ID of the request.
* apSensorTimestampNs is the AP sensor timestamp of the base frame, in nanoseconds.
*/
virtual void onShutter(uint32_t requestId, int64_t apSensorTimestampNs) = 0;
/*
* Invoked when Easel is ready to take another HDR+ request.
*/
virtual void onNextCaptureReady(uint32_t requestId) = 0;
/*
* Invoked when the postview for a request is ready.
*/
virtual void onPostview(uint32_t requestId, std::unique_ptr<std::vector<uint8_t>> postview,
uint32_t width, uint32_t height, uint32_t stride, int32_t format) = 0;
};
} // namespace android
#endif // HDR_PLUS_CLIENT_LISTENER_H

View file

@ -0,0 +1,63 @@
/*
* 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.
*/
#ifndef HDR_PLUS_CLIENT_UTILS_H
#define HDR_PLUS_CLIENT_UTILS_H
#include <utils/Errors.h>
#include "HdrPlusTypes.h"
namespace android {
namespace hdrplus_client_utils {
/*
* Write the image buffer to a .ppm file.
*
* filename is the filename of the .ppm file and should include ".ppm" in the end.
* streamConfig is the stream configuration of the buffer.
* buffer is the buffer to be saved to a .ppm file.
*
* Returns
* OK: if the file is saved successfully.
* BAD_VALUE: if the format is not support or the stream configuration is invalid.
* NO_INIT: if it failed to open the file.
*/
status_t writePpm(const std::string &filename, const pbcamera::StreamConfiguration &streamConfig,
const pbcamera::StreamBuffer &buffer);
/*
* Compare the image buffer against a golden .ppm file.
*
* filename is the filename of the .ppm file and should include ".ppm" in the end.
* streamConfig is the stream configuration of the buffer.
* buffer is the buffer to be compared.
* diffRatio will be the difference ratio between the image buffer and the golden ppm file.
* It's calculated as sum(R, G, B diffs in each pixel) / (width * height * 256 * 3)
*
* Returns
* OK: if the comparison completed successfully.
* BAD_VALUE: if the format is not support or the stream configuration is invalid, or the
* file cannot be parsed correctly.
* NO_INIT: if it failed to open the file.
*/
status_t comparePpm(const std::string &filename, const pbcamera::StreamConfiguration &streamConfig,
const pbcamera::StreamBuffer &buffer, float *diffRatio);
} // hdrplus_client_utils
} // namespace android
#endif // HDR_PLUS_CLIENT_UTILS_H