Merge "Fixes regarding to comments in ag/2839267"

This commit is contained in:
Yi Jin
2017-09-14 17:23:48 +00:00
committed by Android (Google) Code Review
11 changed files with 141 additions and 111 deletions

View File

@@ -42,6 +42,7 @@ cc_library {
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/procrank.proto",
"core/proto/android/service/graphicsstats.proto",
"libs/incident/proto/android/privacy.proto",
],
shared: {
enabled: false,

View File

@@ -27,15 +27,15 @@ const size_t BUFFER_SIZE = 4 * 1024; // 4 KB
* Return the number of bytes of the varint.
*/
static uint32_t
read_raw_varint(FdBuffer::iterator& it)
read_raw_varint(FdBuffer::iterator* it)
{
uint32_t val = 0;
int i = 0;
bool hasNext = true;
while (hasNext) {
hasNext = ((*it & 0x80) != 0);
val += (*it & 0x7F) << (7*i);
it++;
hasNext = ((**it & 0x80) != 0);
val += (**it & 0x7F) << (7*i);
(*it)++;
i++;
}
return val;
@@ -46,21 +46,21 @@ read_raw_varint(FdBuffer::iterator& it)
* If skip is set to true, no data will be written to buf. Return number of bytes written.
*/
static size_t
write_field_or_skip(FdBuffer::iterator &iterator, vector<uint8_t> &buf, uint8_t wireType, bool skip)
write_field_or_skip(FdBuffer::iterator* iter, vector<uint8_t>* buf, uint8_t wireType, bool skip)
{
FdBuffer::iterator snapshot = iterator.snapshot();
FdBuffer::iterator snapshot = iter->snapshot();
size_t bytesToWrite = 0;
uint32_t varint = 0;
switch (wireType) {
case WIRE_TYPE_VARINT:
varint = read_raw_varint(iterator);
varint = read_raw_varint(iter);
if(!skip) return write_raw_varint(buf, varint);
break;
case WIRE_TYPE_FIXED64:
bytesToWrite = 8;
break;
case WIRE_TYPE_LENGTH_DELIMITED:
bytesToWrite = read_raw_varint(iterator);
bytesToWrite = read_raw_varint(iter);
if(!skip) write_raw_varint(buf, bytesToWrite);
break;
case WIRE_TYPE_FIXED32:
@@ -68,14 +68,14 @@ write_field_or_skip(FdBuffer::iterator &iterator, vector<uint8_t> &buf, uint8_t
break;
}
if (skip) {
iterator += bytesToWrite;
*iter += bytesToWrite;
} else {
for (size_t i=0; i<bytesToWrite; i++) {
buf.push_back(*iterator);
iterator++;
buf->push_back(**iter);
(*iter)++;
}
}
return skip ? 0 : iterator - snapshot;
return skip ? 0 : *iter - snapshot;
}
/**
@@ -86,30 +86,30 @@ write_field_or_skip(FdBuffer::iterator &iterator, vector<uint8_t> &buf, uint8_t
* After exit with NO_ERROR, iterator points to the next protobuf field's head.
*/
static status_t
stripField(FdBuffer::iterator &iterator, vector<uint8_t> &buf, const Privacy* parentPolicy, const PrivacySpec& spec)
stripField(FdBuffer::iterator* iter, vector<uint8_t>* buf, const Privacy* parentPolicy, const PrivacySpec& spec)
{
if (iterator.outOfBound() || parentPolicy == NULL) return BAD_VALUE;
if (iter->outOfBound() || parentPolicy == NULL) return BAD_VALUE;
uint32_t varint = read_raw_varint(iterator);
uint32_t varint = read_raw_varint(iter);
uint8_t wireType = read_wire_type(varint);
uint32_t fieldId = read_field_id(varint);
const Privacy* policy = parentPolicy->lookup(fieldId);
if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
bool skip = !spec.CheckPremission(policy);
size_t amt = buf.size();
size_t amt = buf->size();
if (!skip) amt += write_header(buf, fieldId, wireType);
amt += write_field_or_skip(iterator, buf, wireType, skip); // point to head of next field
return buf.size() != amt ? BAD_VALUE : NO_ERROR;
amt += write_field_or_skip(iter, buf, wireType, skip); // point to head of next field
return buf->size() != amt ? BAD_VALUE : NO_ERROR;
}
// current field is message type and its sub-fields have extra privacy policies
deque<vector<uint8_t>> q;
uint32_t msgSize = read_raw_varint(iterator);
uint32_t msgSize = read_raw_varint(iter);
size_t finalSize = 0;
FdBuffer::iterator start = iterator.snapshot();
while ((iterator - start) != (int)msgSize) {
FdBuffer::iterator start = iter->snapshot();
while ((*iter - start) != (int)msgSize) {
vector<uint8_t> v;
status_t err = stripField(iterator, v, policy, spec);
status_t err = stripField(iter, &v, policy, spec);
if (err != NO_ERROR) return err;
if (v.empty()) continue;
q.push_back(v);
@@ -118,11 +118,11 @@ stripField(FdBuffer::iterator &iterator, vector<uint8_t> &buf, const Privacy* pa
write_header(buf, fieldId, wireType);
write_raw_varint(buf, finalSize);
buf.reserve(finalSize);
buf->reserve(finalSize); // reserve the size of the field
while (!q.empty()) {
vector<uint8_t> subField = q.front();
for (vector<uint8_t>::iterator it = subField.begin(); it != subField.end(); it++) {
buf.push_back(*it);
buf->push_back(*it);
}
q.pop_front();
}
@@ -156,7 +156,7 @@ EncodedBuffer::strip(const PrivacySpec& spec)
field.reserve(BUFFER_SIZE);
while (it != mFdBuffer.end()) {
status_t err = stripField(it, field, mPolicy, spec);
status_t err = stripField(&it, &field, mPolicy, spec);
if (err != NO_ERROR) return err;
if (field.size() > BUFFER_SIZE) { // rotate to another chunk if buffer size exceeds
mBuffers.push_back(field);

View File

@@ -16,6 +16,8 @@
#include "Privacy.h"
#include <stdlib.h>
// DESTINATION enum value
const uint8_t DEST_LOCAL = 0;
const uint8_t DEST_EXPLICIT = 1;
@@ -25,33 +27,6 @@ const uint8_t DEST_AUTOMATIC = 2;
const uint8_t TYPE_STRING = 9;
const uint8_t TYPE_MESSAGE = 11;
Privacy::Privacy(uint32_t field_id, uint8_t type, uint8_t dest)
: field_id(field_id),
type(type),
children(NULL),
dest(dest),
patterns(NULL)
{
}
Privacy::Privacy(uint32_t field_id, const Privacy** children)
: field_id(field_id),
type(TYPE_MESSAGE),
children(children),
dest(DEST_DEFAULT_VALUE), // this will be ignored
patterns(NULL)
{
}
Privacy::Privacy(uint32_t field_id, uint8_t dest, const char** patterns)
: field_id(field_id),
type(TYPE_STRING),
children(NULL),
dest(dest),
patterns(patterns)
{
}
bool
Privacy::IsMessageType() const { return type == TYPE_MESSAGE; }

View File

@@ -30,17 +30,13 @@ struct Privacy {
uint32_t field_id;
uint8_t type;
// ignore parent's privacy flags if children are set, NULL-terminated
const Privacy** children;
Privacy** children;
// the following fields are identitical to
// frameworks/base/libs/incident/proto/android/privacy.proto
uint8_t dest;
const char** patterns; // only set when type is string
Privacy(uint32_t field_id, uint8_t type, uint8_t dest); // generic constructor
Privacy(uint32_t field_id, const Privacy** children); // used for message type
Privacy(uint32_t field_id, uint8_t dest, const char** patterns); // used for string type
bool IsMessageType() const;
bool IsStringType() const;
bool HasChildren() const;

View File

@@ -97,13 +97,19 @@ static status_t wait_child(pid_t pid) {
static const Privacy*
get_privacy_of_section(int id)
{
if (id < 0) return NULL;
int i=0;
while (PRIVACY_POLICY_LIST[i] != NULL) {
const Privacy* p = PRIVACY_POLICY_LIST[i];
if (p->field_id == (uint32_t)id) return p;
if (p->field_id > (uint32_t)id) return NULL;
i++;
int l = 0;
int r = PRIVACY_POLICY_COUNT - 1;
while (l <= r) {
int mid = (l + r) >> 1;
const Privacy* p = PRIVACY_POLICY_LIST[mid];
if (p->field_id < (uint32_t)id) {
l = mid + 1;
} else if (p->field_id > (uint32_t)id) {
r = mid - 1;
} else {
return p;
}
}
return NULL;
}

View File

@@ -50,23 +50,23 @@ write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
}
size_t
write_raw_varint(vector<uint8_t> &buf, uint32_t val)
write_raw_varint(vector<uint8_t>* buf, uint32_t val)
{
size_t size = 0;
while (true) {
size++;
if ((val & ~0x7F) == 0) {
buf.push_back((uint8_t) val);
buf->push_back((uint8_t) val);
return size;
} else {
buf.push_back((uint8_t)((val & 0x7F) | 0x80));
buf->push_back((uint8_t)((val & 0x7F) | 0x80));
val >>= 7;
}
}
}
size_t
write_header(vector<uint8_t> &buf, uint32_t fieldId, uint8_t wireType)
write_header(vector<uint8_t>* buf, uint32_t fieldId, uint8_t wireType)
{
return write_raw_varint(buf, (fieldId << 3) | wireType);
}

View File

@@ -53,12 +53,12 @@ uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_
/**
* Write a varint into a vector. Return the size of the varint.
*/
size_t write_raw_varint(vector<uint8_t> &buf, uint32_t val);
size_t write_raw_varint(vector<uint8_t>* buf, uint32_t val);
/**
* Write a protobuf header. Return the size of the header.
*/
size_t write_header(vector<uint8_t> &buf, uint32_t fieldId, uint8_t wireType);
size_t write_header(vector<uint8_t>* buf, uint32_t fieldId, uint8_t wireType);
enum {
// IncidentProto.header

View File

@@ -22,15 +22,17 @@
/**
* This is the mapping of section IDs to the commands that are run to get those commands.
* The section IDs are guaranteed in ascending order
* The section IDs are guaranteed in ascending order, NULL-terminated.
*/
extern const Section* SECTION_LIST[];
/**
* This is the mapping of section IDs to each section's privacy policy.
* The section IDs are guaranteed in ascending order
* The section IDs are guaranteed in ascending order, not NULL-terminated since size is provided.
*/
extern const Privacy* PRIVACY_POLICY_LIST[];
extern const int PRIVACY_POLICY_COUNT;
#endif // SECTION_LIST_H

View File

@@ -42,6 +42,38 @@ const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1
const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1
const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2;
static Privacy* create_privacy(uint32_t field_id, uint8_t type, uint8_t dest) {
struct Privacy* p = (struct Privacy*)malloc(sizeof(struct Privacy));
p->field_id = field_id;
p->type = type;
p->children = NULL;
p->dest = dest;
p->patterns = NULL;
return p;
}
static Privacy* create_message_privacy(uint32_t field_id, Privacy** children)
{
struct Privacy* p = (struct Privacy*)malloc(sizeof(struct Privacy));
p->field_id = field_id;
p->type = MESSAGE_TYPE;
p->children = children;
p->dest = EXPLICIT;
p->patterns = NULL;
return p;
}
static Privacy* create_string_privacy(uint32_t field_id, uint8_t dest, const char** patterns)
{
struct Privacy* p = (struct Privacy*)malloc(sizeof(struct Privacy));
p->field_id = field_id;
p->type = STRING_TYPE;
p->children = NULL;
p->dest = dest;
p->patterns = patterns;
return p;
}
class EncodedBufferTest : public Test {
public:
virtual void SetUp() override {
@@ -78,7 +110,7 @@ public:
}
va_end(args);
list[size] = NULL;
assertStrip(dest, expected, new Privacy(300, const_cast<const Privacy**>(list)));
assertStrip(dest, expected, create_message_privacy(300, list));
}
FdBuffer buffer;
@@ -88,62 +120,62 @@ private:
TEST_F(EncodedBufferTest, NullFieldPolicy) {
writeToFdBuffer(STRING_FIELD_0);
assertStrip(EXPLICIT, STRING_FIELD_0, new Privacy(300, NULL));
assertStrip(EXPLICIT, STRING_FIELD_0, create_string_privacy(300, AUTOMATIC, NULL));
}
TEST_F(EncodedBufferTest, StripSpecNotAllowed) {
writeToFdBuffer(STRING_FIELD_0);
assertStripByFields(AUTOMATIC, "", 1, new Privacy(0, STRING_TYPE, EXPLICIT));
assertStripByFields(AUTOMATIC, "", 1, create_privacy(0, STRING_TYPE, EXPLICIT));
}
TEST_F(EncodedBufferTest, StripVarintField) {
writeToFdBuffer(VARINT_FIELD_1);
assertStripByFields(EXPLICIT, "", 1, new Privacy(1, OTHER_TYPE, LOCAL));
assertStripByFields(EXPLICIT, "", 1, create_privacy(1, OTHER_TYPE, LOCAL));
}
TEST_F(EncodedBufferTest, StripLengthDelimitedField_String) {
writeToFdBuffer(STRING_FIELD_2);
assertStripByFields(EXPLICIT, "", 1, new Privacy(2, STRING_TYPE, LOCAL));
assertStripByFields(EXPLICIT, "", 1, create_privacy(2, STRING_TYPE, LOCAL));
}
TEST_F(EncodedBufferTest, StripFixed64Field) {
writeToFdBuffer(FIX64_FIELD_3);
assertStripByFields(EXPLICIT, "", 1, new Privacy(3, OTHER_TYPE, LOCAL));
assertStripByFields(EXPLICIT, "", 1, create_privacy(3, OTHER_TYPE, LOCAL));
}
TEST_F(EncodedBufferTest, StripFixed32Field) {
writeToFdBuffer(FIX32_FIELD_4);
assertStripByFields(EXPLICIT, "", 1, new Privacy(4, OTHER_TYPE, LOCAL));
assertStripByFields(EXPLICIT, "", 1, create_privacy(4, OTHER_TYPE, LOCAL));
}
TEST_F(EncodedBufferTest, StripLengthDelimitedField_Message) {
writeToFdBuffer(MESSAGE_FIELD_5);
assertStripByFields(EXPLICIT, "", 1, new Privacy(5, MESSAGE_TYPE, LOCAL));
assertStripByFields(EXPLICIT, "", 1, create_privacy(5, MESSAGE_TYPE, LOCAL));
}
TEST_F(EncodedBufferTest, NoStripVarintField) {
writeToFdBuffer(VARINT_FIELD_1);
assertStripByFields(EXPLICIT, VARINT_FIELD_1, 1, new Privacy(1, OTHER_TYPE, AUTOMATIC));
assertStripByFields(EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, AUTOMATIC));
}
TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_String) {
writeToFdBuffer(STRING_FIELD_2);
assertStripByFields(EXPLICIT, STRING_FIELD_2, 1, new Privacy(2, STRING_TYPE, AUTOMATIC));
assertStripByFields(EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, AUTOMATIC));
}
TEST_F(EncodedBufferTest, NoStripFixed64Field) {
writeToFdBuffer(FIX64_FIELD_3);
assertStripByFields(EXPLICIT, FIX64_FIELD_3, 1, new Privacy(3, OTHER_TYPE, AUTOMATIC));
assertStripByFields(EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, AUTOMATIC));
}
TEST_F(EncodedBufferTest, NoStripFixed32Field) {
writeToFdBuffer(FIX32_FIELD_4);
assertStripByFields(EXPLICIT, FIX32_FIELD_4, 1, new Privacy(4, OTHER_TYPE, AUTOMATIC));
assertStripByFields(EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, AUTOMATIC));
}
TEST_F(EncodedBufferTest, NoStripLengthDelimitedField_Message) {
writeToFdBuffer(MESSAGE_FIELD_5);
assertStripByFields(EXPLICIT, MESSAGE_FIELD_5, 1, new Privacy(5, MESSAGE_TYPE, AUTOMATIC));
assertStripByFields(EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, AUTOMATIC));
}
TEST_F(EncodedBufferTest, StripVarintAndString) {
@@ -151,7 +183,7 @@ TEST_F(EncodedBufferTest, StripVarintAndString) {
+ FIX64_FIELD_3 + FIX32_FIELD_4);
string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4;
assertStripByFields(EXPLICIT, expected, 2,
new Privacy(1, OTHER_TYPE, LOCAL), new Privacy(2, STRING_TYPE, LOCAL));
create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(2, STRING_TYPE, LOCAL));
}
TEST_F(EncodedBufferTest, StripVarintAndFixed64) {
@@ -159,28 +191,28 @@ TEST_F(EncodedBufferTest, StripVarintAndFixed64) {
+ FIX64_FIELD_3 + FIX32_FIELD_4);
string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4;
assertStripByFields(EXPLICIT, expected, 2,
new Privacy(1, OTHER_TYPE, LOCAL), new Privacy(3, OTHER_TYPE, LOCAL));
create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(3, OTHER_TYPE, LOCAL));
}
TEST_F(EncodedBufferTest, StripVarintInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5);
const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
assertStripByFields(EXPLICIT, expected, 1, new Privacy(5, list));
assertStripByFields(EXPLICIT, expected, 1, create_message_privacy(5, list));
}
TEST_F(EncodedBufferTest, StripFix64AndVarintInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5);
const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
assertStripByFields(EXPLICIT, expected, 2, new Privacy(3, OTHER_TYPE, LOCAL), new Privacy(5, list));
assertStripByFields(EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, LOCAL), create_message_privacy(5, list));
}
TEST_F(EncodedBufferTest, ClearAndStrip) {
string data = STRING_FIELD_0 + VARINT_FIELD_1;
writeToFdBuffer(data);
const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
EncodedBuffer encodedBuf(buffer, new Privacy(300, list));
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
EncodedBuffer encodedBuf(buffer, create_message_privacy(300, list));
PrivacySpec spec1(EXPLICIT), spec2(LOCAL);
ASSERT_EQ(encodedBuf.strip(spec1), NO_ERROR);
@@ -191,17 +223,17 @@ TEST_F(EncodedBufferTest, ClearAndStrip) {
TEST_F(EncodedBufferTest, BadDataInFdBuffer) {
writeToFdBuffer("iambaddata");
const Privacy* list[] = { new Privacy(4, OTHER_TYPE, AUTOMATIC), NULL };
EncodedBuffer encodedBuf(buffer, new Privacy(300, list));
Privacy* list[] = { create_privacy(4, OTHER_TYPE, AUTOMATIC), NULL };
EncodedBuffer encodedBuf(buffer, create_message_privacy(300, list));
PrivacySpec spec;
ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
}
TEST_F(EncodedBufferTest, BadDataInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe");
const Privacy* list[] = { new Privacy(1, OTHER_TYPE, LOCAL), NULL };
const Privacy* field5[] = { new Privacy(5, list), NULL };
EncodedBuffer encodedBuf(buffer, new Privacy(300, field5));
Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
Privacy* field5[] = { create_message_privacy(5, list), NULL };
EncodedBuffer encodedBuf(buffer, create_message_privacy(300, field5));
PrivacySpec spec;
ASSERT_EQ(encodedBuf.strip(spec), BAD_VALUE);
}

View File

@@ -9,13 +9,20 @@ const uint8_t LOCAL = 0;
const uint8_t EXPLICIT = 1;
const uint8_t AUTOMATIC = 2;
const Privacy* list[] = {
new Privacy(1, 1, LOCAL),
new Privacy(2, AUTOMATIC, (const char**)NULL),
Privacy sub_field_1 { 1, 1, NULL, LOCAL, NULL };
Privacy sub_field_2 { 2, 9, NULL, AUTOMATIC, NULL };
Privacy* list[] = {
&sub_field_1,
&sub_field_2,
NULL };
Privacy field_0 { 0, 11, list, EXPLICIT, NULL };
Privacy field_1 { 1, 9, NULL, AUTOMATIC, NULL };
const Privacy* PRIVACY_POLICY_LIST[] = {
new Privacy(0, list),
new Privacy(1, 9, AUTOMATIC),
NULL
};
&field_0,
&field_1
};
const int PRIVACY_POLICY_COUNT = 2;

View File

@@ -135,7 +135,7 @@ static bool generatePrivacyFlags(const Descriptor* descriptor, const char* alias
if (generatePrivacyFlags(field->message_type(), field_name, msgNames) &&
isDefaultDest(field)) break;
printf("Privacy %s(%d, %s_LIST);\n", field_name, field->number(), field_name);
printf("Privacy %s { %d, %d, %s_LIST, %d, NULL };\n", field_name, field->number(), field->type(), field_name, p.dest());
hasDefaultFlags[i] = false;
break;
case FieldDescriptor::TYPE_STRING:
@@ -147,12 +147,12 @@ static bool generatePrivacyFlags(const Descriptor* descriptor, const char* alias
printf(" \"%s\",\n", replaceAll(p.patterns(i), '\\', "\\\\").c_str());
}
printf(" NULL };\n");
printf("Privacy %s(%d, %d, %s_patterns);\n", field_name, field->number(), p.dest(), field_name);
printf("Privacy %s { %d, %d, NULL, %d, %s_patterns };\n", field_name, field->number(), field->type(), p.dest(), field_name);
hasDefaultFlags[i] = false;
break;
default:
if (isDefaultDest(field)) break;
printf("Privacy %s(%d, %d, %d);\n", field_name, field->number(), (int) field->type(), p.dest());
printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", field_name, field->number(), field->type(), p.dest());
hasDefaultFlags[i] = false;
}
// add the field name to message map, true means it has default flags
@@ -166,13 +166,23 @@ static bool generatePrivacyFlags(const Descriptor* descriptor, const char* alias
if (allDefaults) return true;
emptyline();
printf("const Privacy* %s_LIST[] = {\n", alias);
bool needConst = strcmp(alias, "PRIVACY_POLICY") == 0;
int policyCount = 0;
printf("%s Privacy* %s_LIST[] = {\n", needConst ? "const" : "", alias);
for (int i=0; i<descriptor->field_count(); i++) {
const FieldDescriptor* field = descriptor->field(i);
if (hasDefaultFlags[i]) continue;
printf(" &%s,\n", replaceAll(field->full_name(), '.', "__").c_str());
policyCount++;
}
if (needConst) {
printf("};\n\n");
printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount);
} else {
printf(" NULL };\n");
}
printf(" NULL };\n");
emptyline();
return false;
}
@@ -214,7 +224,8 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
map<string, bool> messageNames;
if (generatePrivacyFlags(descriptor, "PRIVACY_POLICY", messageNames)) {
// if no privacy options set at all, define an empty list
printf("const Privacy* PRIVACY_POLICY_LIST[] = { NULL };\n");
printf("const Privacy* PRIVACY_POLICY_LIST[] = {};\n");
printf("const int PRIVACY_POLICY_COUNT = 0;\n");
}
return true;