Merge "Fix support for negatively Z-ordered SubWindows."
diff --git a/Android.bp b/Android.bp
index 2ea4894..d8027c1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -65,6 +65,7 @@
"core/proto/android/os/procrank.proto",
"core/proto/android/os/system_properties.proto",
"core/proto/android/service/graphicsstats.proto",
+ "libs/incident/proto/android/privacy.proto",
"tools/streaming_proto/stream.proto",
],
shared: {
diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
index 140b12c..5db2239 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -16,37 +16,18 @@
#include "Privacy.h"
+#include <android/os/IncidentReportArgs.h>
#include <stdlib.h>
-// DESTINATION enum value
-const uint8_t DEST_LOCAL = 0;
-const uint8_t DEST_EXPLICIT = 1;
-const uint8_t DEST_AUTOMATIC = 2;
+uint64_t encode_field_id(const Privacy* p) { return (uint64_t)p->type << 32 | p->field_id; }
-// type of the field, identitical to protobuf definition
-const uint8_t TYPE_STRING = 9;
-const uint8_t TYPE_MESSAGE = 11;
-
-bool
-Privacy::IsMessageType() const { return type == TYPE_MESSAGE; }
-
-uint64_t
-Privacy::EncodedFieldId() const { return (uint64_t)type << 32 | field_id; }
-
-bool
-Privacy::IsStringType() const { return type == TYPE_STRING; }
-
-bool
-Privacy::HasChildren() const { return children != NULL && children[0] != NULL; }
-
-const Privacy*
-Privacy::lookup(uint32_t fieldId) const
+const Privacy* lookup(const Privacy* p, uint32_t fieldId)
{
- if (children == NULL) return NULL;
- for (int i=0; children[i] != NULL; i++) {
- if (children[i]->field_id == fieldId) return children[i];
- // This assumes the list's field id is in ascending order and must be true.
- if (children[i]->field_id > fieldId) return NULL;
+ if (p->children == NULL) return NULL;
+ for (int i=0; p->children[i] != NULL; i++) { // NULL-terminated.
+ if (p->children[i]->field_id == fieldId) return p->children[i];
+ // Incident section gen tool guarantees field ids in ascending order.
+ if (p->children[i]->field_id > fieldId) return NULL;
}
return NULL;
}
@@ -54,11 +35,14 @@
static bool allowDest(const uint8_t dest, const uint8_t policy)
{
switch (policy) {
- case DEST_LOCAL:
- return dest == DEST_LOCAL;
- case DEST_EXPLICIT:
- return dest == DEST_LOCAL || dest == DEST_EXPLICIT;
- case DEST_AUTOMATIC:
+ case android::os::DEST_LOCAL:
+ return dest == android::os::DEST_LOCAL;
+ case android::os::DEST_EXPLICIT:
+ case DEST_UNSET:
+ return dest == android::os::DEST_LOCAL ||
+ dest == android::os::DEST_EXPLICIT ||
+ dest == DEST_UNSET;
+ case android::os::DEST_AUTOMATIC:
return true;
default:
return false;
@@ -72,18 +56,19 @@
}
bool
-PrivacySpec::CheckPremission(const Privacy* privacy) const
+PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const
{
- uint8_t policy = privacy == NULL ? DEST_DEFAULT_VALUE : privacy->dest;
+ uint8_t policy = privacy != NULL ? privacy->dest : defaultDest;
return allowDest(dest, policy);
}
bool
-PrivacySpec::RequireAll() const { return dest == DEST_LOCAL; }
+PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; }
-PrivacySpec new_spec_from_args(int dest) {
+PrivacySpec new_spec_from_args(int dest)
+{
if (dest < 0) return PrivacySpec();
return PrivacySpec(dest);
}
-PrivacySpec get_default_dropbox_spec() { return PrivacySpec(DEST_AUTOMATIC); }
\ No newline at end of file
+PrivacySpec get_default_dropbox_spec() { return PrivacySpec(android::os::DEST_AUTOMATIC); }
\ No newline at end of file
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
index f514f19..9e15ff4 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -19,35 +19,46 @@
#include <stdint.h>
-// This is the default value of DEST enum
-const uint8_t DEST_DEFAULT_VALUE = 1;
+// This is the default value of DEST enum, sync with privacy.proto
+const uint8_t DEST_UNSET = 255; // DEST_UNSET is not exposed to libincident
+const uint8_t DEST_DEFAULT_VALUE = DEST_UNSET;
/*
- * In order not to depend on libprotobuf-cpp-full nor libplatformprotos in incidentd,
- * privacy options's data structure are explicitly redefined in this file.
+ * In order to NOT auto-generate large chuck of code by proto compiler in incidentd,
+ * privacy options's data structure are explicitly redefined here and
+ * the values are populated by incident_section_gen tool.
+ *
+ * Each proto field will have a Privacy when it is different from its parent, otherwise
+ * it uses its parent's tag. A message type will have an array of Privacy.
*/
struct Privacy {
+ // The field number
uint32_t field_id;
+
+ // The field type, see external/protobuf/src/google/protobuf/descriptor.h
uint8_t type;
- // ignore parent's privacy flags if children are set, NULL-terminated
+
+ // If children is null, it is a primitive field,
+ // otherwise it is a message field which could have overridden privacy tags here.
+ // This array is NULL-terminated.
Privacy** children;
- // the following fields are identitical to
- // frameworks/base/libs/incident/proto/android/privacy.proto
+ // DESTINATION Enum in frameworks/base/libs/incident/proto/android/privacy.proto.
uint8_t dest;
- const char** patterns; // only set when type is string
-
- bool IsMessageType() const;
- bool IsStringType() const;
- bool HasChildren() const;
- uint64_t EncodedFieldId() const;
-
- const Privacy* lookup(uint32_t fieldId) const;
+ // A list of regexp rules for stripping string fields in proto.
+ const char** patterns;
};
+// Encode field id used by ProtoOutputStream.
+uint64_t encode_field_id(const Privacy* p);
+
+// Look up the child with given fieldId, if not found, return NULL.
+const Privacy* lookup(const Privacy* p, uint32_t fieldId);
+
/**
* PrivacySpec defines the request has what level of privacy authorization.
* For example, a device without user consent should only be able to upload AUTOMATIC fields.
+ * DEST_UNSET are treated as DEST_EXPLICIT.
*/
class PrivacySpec {
public:
@@ -58,7 +69,10 @@
bool operator<(const PrivacySpec& other) const;
- bool CheckPremission(const Privacy* privacy) const;
+ // check permission of a policy, if returns true, don't strip the data.
+ bool CheckPremission(const Privacy* privacy, const uint8_t defaultDest = DEST_DEFAULT_VALUE) const;
+
+ // if returns true, no data need to be stripped.
bool RequireAll() const;
};
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index 77ae1a7..03faa92 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-
+#define LOG_TAG "incidentd"
#include "PrivacyBuffer.h"
#include "io_util.h"
#include <android/util/protobuf.h>
+#include <cutils/log.h>
using namespace android::util;
+const bool DEBUG = false;
+
/**
* Write the field to buf based on the wire type, iterator will point to next field.
* If skip is set to true, no data will be written to buf. Return number of bytes written.
@@ -30,6 +33,9 @@
void
PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip)
{
+ if (DEBUG) ALOGD("%s field %d (wiretype = %d)", skip ? "skip" : "write",
+ read_field_id(fieldTag), read_wire_type(fieldTag));
+
uint8_t wireType = read_wire_type(fieldTag);
size_t bytesToWrite = 0;
uint32_t varint = 0;
@@ -55,6 +61,7 @@
bytesToWrite = 4;
break;
}
+ if (DEBUG) ALOGD("%s %d bytes of data", skip ? "skip" : "write", (int)bytesToWrite);
if (skip) {
mData.rp()->move(bytesToWrite);
} else {
@@ -76,10 +83,13 @@
{
if (!mData.hasNext() || parentPolicy == NULL) return BAD_VALUE;
uint32_t fieldTag = mData.readRawVarint();
- const Privacy* policy = parentPolicy->lookup(read_field_id(fieldTag));
+ const Privacy* policy = lookup(parentPolicy, read_field_id(fieldTag));
- if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
- bool skip = !spec.CheckPremission(policy);
+ if (policy == NULL || policy->children == NULL) {
+ if (DEBUG) ALOGD("Not a message field %d: dest(%d)", read_field_id(fieldTag),
+ policy != NULL ? policy->dest : parentPolicy->dest);
+
+ bool skip = !spec.CheckPremission(policy, parentPolicy->dest);
// iterator will point to head of next field
writeFieldOrSkip(fieldTag, skip);
return NO_ERROR;
@@ -87,7 +97,7 @@
// current field is message type and its sub-fields have extra privacy policies
uint32_t msgSize = mData.readRawVarint();
EncodedBuffer::Pointer start = mData.rp()->copy();
- long long token = mProto.start(policy->EncodedFieldId());
+ long long token = mProto.start(encode_field_id(policy));
while (mData.rp()->pos() - start.pos() != msgSize) {
status_t err = stripField(policy, spec);
if (err != NO_ERROR) return err;
@@ -112,8 +122,9 @@
status_t
PrivacyBuffer::strip(const PrivacySpec& spec)
{
+ if (DEBUG) ALOGD("Strip with spec %d", spec.dest);
// optimization when no strip happens
- if (mPolicy == NULL || !mPolicy->HasChildren() || spec.RequireAll()) {
+ if (mPolicy == NULL || mPolicy->children == NULL || spec.RequireAll()) {
if (spec.CheckPremission(mPolicy)) mSize = mData.size();
return NO_ERROR;
}
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index c08b9ea..1bf795b 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -210,9 +210,9 @@
{
for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) {
const sp<ReportRequest> request = *it;
- const vector<vector<int8_t>>& headers = request->args.headers();
+ const vector<vector<uint8_t>>& headers = request->args.headers();
- for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) {
+ for (vector<vector<uint8_t>>::const_iterator buf=headers.begin(); buf!=headers.end(); buf++) {
if (buf->empty()) continue;
// So the idea is only requests with negative fd are written to dropbox file.
diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp
index f043d36..af4a35c 100644
--- a/cmds/incidentd/src/io_util.cpp
+++ b/cmds/incidentd/src/io_util.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "incidentd"
+
#include "io_util.h"
#include <unistd.h>
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index ca94623..32b9e42 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -12,27 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#define LOG_TAG "incidentd"
+
#include "FdBuffer.h"
#include "PrivacyBuffer.h"
#include <android-base/file.h>
#include <android-base/test_utils.h>
+#include <android/os/IncidentReportArgs.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string.h>
using namespace android;
using namespace android::base;
+using namespace android::os;
using namespace std;
using ::testing::StrEq;
using ::testing::Test;
using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStdout;
-const uint8_t LOCAL = 0;
-const uint8_t EXPLICIT = 1;
-const uint8_t AUTOMATIC = 2;
-
const uint8_t OTHER_TYPE = 1;
const uint8_t STRING_TYPE = 9;
const uint8_t MESSAGE_TYPE = 11;
@@ -109,21 +109,11 @@
p->field_id = field_id;
p->type = MESSAGE_TYPE;
p->children = children;
- p->dest = EXPLICIT;
+ p->dest = DEST_DEFAULT_VALUE;
p->patterns = NULL;
return p;
}
- Privacy* create_string_privacy(uint32_t field_id, uint8_t dest, const char** patterns) {
- Privacy* p = new_uninit_privacy();
- p->field_id = field_id;
- p->type = STRING_TYPE;
- p->children = NULL;
- p->dest = dest;
- p->patterns = patterns;
- return p;
- }
-
FdBuffer buffer;
private:
TemporaryFile tf;
@@ -138,103 +128,103 @@
}
};
-TEST_F(PrivacyBufferTest, NullFieldPolicy) {
+TEST_F(PrivacyBufferTest, NullPolicy) {
writeToFdBuffer(STRING_FIELD_0);
- assertStrip(EXPLICIT, STRING_FIELD_0, create_string_privacy(300, AUTOMATIC, NULL));
+ assertStrip(DEST_EXPLICIT, STRING_FIELD_0, NULL);
}
-TEST_F(PrivacyBufferTest, StripSpecNotAllowed) {
+TEST_F(PrivacyBufferTest, StripUnsetField) {
writeToFdBuffer(STRING_FIELD_0);
- assertStripByFields(AUTOMATIC, "", 1, create_privacy(0, STRING_TYPE, EXPLICIT));
+ assertStripByFields(DEST_AUTOMATIC, "", 1, create_privacy(0, STRING_TYPE, DEST_UNSET));
}
TEST_F(PrivacyBufferTest, StripVarintField) {
writeToFdBuffer(VARINT_FIELD_1);
- assertStripByFields(EXPLICIT, "", 1, create_privacy(1, OTHER_TYPE, LOCAL));
+ assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(1, OTHER_TYPE, DEST_LOCAL));
}
TEST_F(PrivacyBufferTest, StripLengthDelimitedField_String) {
writeToFdBuffer(STRING_FIELD_2);
- assertStripByFields(EXPLICIT, "", 1, create_privacy(2, STRING_TYPE, LOCAL));
+ assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(2, STRING_TYPE, DEST_LOCAL));
}
TEST_F(PrivacyBufferTest, StripFixed64Field) {
writeToFdBuffer(FIX64_FIELD_3);
- assertStripByFields(EXPLICIT, "", 1, create_privacy(3, OTHER_TYPE, LOCAL));
+ assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(3, OTHER_TYPE, DEST_LOCAL));
}
TEST_F(PrivacyBufferTest, StripFixed32Field) {
writeToFdBuffer(FIX32_FIELD_4);
- assertStripByFields(EXPLICIT, "", 1, create_privacy(4, OTHER_TYPE, LOCAL));
+ assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(4, OTHER_TYPE, DEST_LOCAL));
}
TEST_F(PrivacyBufferTest, StripLengthDelimitedField_Message) {
writeToFdBuffer(MESSAGE_FIELD_5);
- assertStripByFields(EXPLICIT, "", 1, create_privacy(5, MESSAGE_TYPE, LOCAL));
+ assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(5, MESSAGE_TYPE, DEST_LOCAL));
}
TEST_F(PrivacyBufferTest, NoStripVarintField) {
writeToFdBuffer(VARINT_FIELD_1);
- assertStripByFields(EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, AUTOMATIC));
+ assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1, create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC));
}
TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_String) {
writeToFdBuffer(STRING_FIELD_2);
- assertStripByFields(EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, AUTOMATIC));
+ assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1, create_privacy(2, STRING_TYPE, DEST_AUTOMATIC));
}
TEST_F(PrivacyBufferTest, NoStripFixed64Field) {
writeToFdBuffer(FIX64_FIELD_3);
- assertStripByFields(EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, AUTOMATIC));
+ assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1, create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC));
}
TEST_F(PrivacyBufferTest, NoStripFixed32Field) {
writeToFdBuffer(FIX32_FIELD_4);
- assertStripByFields(EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, AUTOMATIC));
+ assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1, create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC));
}
TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_Message) {
writeToFdBuffer(MESSAGE_FIELD_5);
- assertStripByFields(EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, AUTOMATIC));
+ assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1, create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC));
}
TEST_F(PrivacyBufferTest, StripVarintAndString) {
writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
+ FIX64_FIELD_3 + FIX32_FIELD_4);
string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4;
- assertStripByFields(EXPLICIT, expected, 2,
- create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(2, STRING_TYPE, LOCAL));
+ assertStripByFields(DEST_EXPLICIT, expected, 2,
+ create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(2, STRING_TYPE, DEST_LOCAL));
}
TEST_F(PrivacyBufferTest, StripVarintAndFixed64) {
writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2
+ FIX64_FIELD_3 + FIX32_FIELD_4);
string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4;
- assertStripByFields(EXPLICIT, expected, 2,
- create_privacy(1, OTHER_TYPE, LOCAL), create_privacy(3, OTHER_TYPE, LOCAL));
+ assertStripByFields(DEST_EXPLICIT, expected, 2,
+ create_privacy(1, OTHER_TYPE, DEST_LOCAL), create_privacy(3, OTHER_TYPE, DEST_LOCAL));
}
TEST_F(PrivacyBufferTest, StripVarintInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5);
- Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
+ Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
- assertStripByFields(EXPLICIT, expected, 1, create_message_privacy(5, list));
+ assertStripByFields(DEST_EXPLICIT, expected, 1, create_message_privacy(5, list));
}
TEST_F(PrivacyBufferTest, StripFix64AndVarintInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5);
- Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
+ Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2;
- assertStripByFields(EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, LOCAL), create_message_privacy(5, list));
+ assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL), create_message_privacy(5, list));
}
TEST_F(PrivacyBufferTest, ClearAndStrip) {
string data = STRING_FIELD_0 + VARINT_FIELD_1;
writeToFdBuffer(data);
- Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
+ Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
EncodedBuffer::iterator bufData = buffer.data();
PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
- PrivacySpec spec1(EXPLICIT), spec2(LOCAL);
+ PrivacySpec spec1(DEST_EXPLICIT), spec2(DEST_LOCAL);
ASSERT_EQ(privacyBuf.strip(spec1), NO_ERROR);
assertBuffer(privacyBuf, STRING_FIELD_0);
@@ -244,7 +234,7 @@
TEST_F(PrivacyBufferTest, BadDataInFdBuffer) {
writeToFdBuffer("iambaddata");
- Privacy* list[] = { create_privacy(4, OTHER_TYPE, AUTOMATIC), NULL };
+ Privacy* list[] = { create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL };
EncodedBuffer::iterator bufData = buffer.data();
PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData);
PrivacySpec spec;
@@ -253,7 +243,7 @@
TEST_F(PrivacyBufferTest, BadDataInNestedMessage) {
writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe");
- Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), NULL };
+ Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
Privacy* field5[] = { create_message_privacy(5, list), NULL };
EncodedBuffer::iterator bufData = buffer.data();
PrivacyBuffer privacyBuf(create_message_privacy(300, field5), bufData);
@@ -265,8 +255,17 @@
string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5;
writeToFdBuffer(input);
Privacy* field5 = create_message_privacy(5, NULL);
- Privacy* list[] = { create_privacy(1, OTHER_TYPE, LOCAL), field5, NULL };
+ Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL };
field5->children = list;
string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2;
- assertStrip(EXPLICIT, expected, field5);
+ assertStrip(DEST_EXPLICIT, expected, field5);
}
+
+TEST_F(PrivacyBufferTest, AutoMessage) {
+ writeToFdBuffer(STRING_FIELD_2 + MESSAGE_FIELD_5);
+ Privacy* list[] = { create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL };
+ Privacy* autoMsg = create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC);
+ autoMsg->children = list;
+ string expected = "\x2a\xd" + STRING_FIELD_2;
+ assertStripByFields(DEST_AUTOMATIC, expected, 1, autoMsg);
+}
\ No newline at end of file
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index 8d99fc7..531c9f2 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -141,7 +141,7 @@
IncidentReportArgs args1, args2;
args1.addSection(1);
args2.addSection(2);
- std::vector<int8_t> header {'a', 'b', 'c', 'd', 'e'};
+ std::vector<uint8_t> header {'a', 'b', 'c', 'd', 'e'};
args2.addHeader(header);
sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd);
sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd);
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 649e908..0c7876c 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -66,12 +66,12 @@
args1.addSection(2);
args2.setAll(true);
- vector<int8_t> head1;
+ vector<uint8_t> head1;
head1.push_back('a');
head1.push_back('x');
head1.push_back('e');
- vector<int8_t> head2;
+ vector<uint8_t> head2;
head2.push_back('p');
head2.push_back('u');
head2.push_back('p');
diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp
index 4acc429..1d6213f 100644
--- a/cmds/incidentd/tests/section_list.cpp
+++ b/cmds/incidentd/tests/section_list.cpp
@@ -5,20 +5,16 @@
NULL
};
-const uint8_t LOCAL = 0;
-const uint8_t EXPLICIT = 1;
-const uint8_t AUTOMATIC = 2;
-
-Privacy sub_field_1 { 1, 1, NULL, LOCAL, NULL };
-Privacy sub_field_2 { 2, 9, NULL, AUTOMATIC, NULL };
+Privacy sub_field_1 { 1, 1, NULL, DEST_LOCAL, NULL };
+Privacy sub_field_2 { 2, 9, NULL, DEST_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 };
+Privacy field_0 { 0, 11, list, DEST_EXPLICIT, NULL };
+Privacy field_1 { 1, 9, NULL, DEST_AUTOMATIC, NULL };
Privacy* final_list[] = {
&field_0,
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 068f5f7..01fe5bf 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -680,6 +680,26 @@
* as the user moves between applications and doesn't require a special permission.
* </p>
*
+ * <p>
+ * Recommended naming conventions for tags to make debugging easier:
+ * <ul>
+ * <li>use a unique prefix delimited by a colon for your app/library (e.g.
+ * gmail:mytag) to make it easier to understand where the wake locks comes
+ * from. This namespace will also avoid collision for tags inside your app
+ * coming from different libraries which will make debugging easier.
+ * <li>use constants (e.g. do not include timestamps in the tag) to make it
+ * easier for tools to aggregate similar wake locks. When collecting
+ * debugging data, the platform only monitors a finite number of tags,
+ * using constants will help tools to provide better debugging data.
+ * <li>avoid using Class#getName() or similar method since this class name
+ * can be transformed by java optimizer and obfuscator tools.
+ * <li>avoid wrapping the tag or a prefix to avoid collision with wake lock
+ * tags from the platform (e.g. *alarm*).
+ * <li>never include personnally identifiable information for privacy
+ * reasons.
+ * </ul>
+ * </p>
+ *
* @param levelAndFlags Combination of wake lock level and flag values defining
* the requested behavior of the WakeLock.
* @param tag Your class name (or other tag) for debugging purposes.
@@ -1509,6 +1529,13 @@
* cost of that work can be accounted to the application.
* </p>
*
+ * <p>
+ * Make sure to follow the tag naming convention when using WorkSource
+ * to make it easier for app developers to understand wake locks
+ * attributed to them. See {@link PowerManager#newWakeLock(int, String)}
+ * documentation.
+ * </p>
+ *
* @param ws The work source, or null if none.
*/
public void setWorkSource(WorkSource ws) {
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 17c7ebd..7635a72 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -81,6 +81,7 @@
private int mCollapsibleHeightReserved;
private int mTopOffset;
+ private boolean mShowAtTop;
private boolean mIsDragging;
private boolean mOpenOnClick;
@@ -134,6 +135,7 @@
mMaxCollapsedHeightSmall = a.getDimensionPixelSize(
R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall,
mMaxCollapsedHeight);
+ mShowAtTop = a.getBoolean(R.styleable.ResolverDrawerLayout_showAtTop, false);
a.recycle();
mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
@@ -162,6 +164,16 @@
return mCollapseOffset > 0;
}
+ public void setShowAtTop(boolean showOnTop) {
+ mShowAtTop = showOnTop;
+ invalidate();
+ requestLayout();
+ }
+
+ public boolean getShowAtTop() {
+ return mShowAtTop;
+ }
+
public void setCollapsed(boolean collapsed) {
if (!isLaidOut()) {
mOpenOnLayout = collapsed;
@@ -206,6 +218,12 @@
return false;
}
+ if (getShowAtTop()) {
+ // Keep the drawer fully open.
+ mCollapseOffset = 0;
+ return false;
+ }
+
if (isLaidOut()) {
final boolean isCollapsedOld = mCollapseOffset != 0;
if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight
@@ -372,14 +390,23 @@
mVelocityTracker.computeCurrentVelocity(1000);
final float yvel = mVelocityTracker.getYVelocity(mActivePointerId);
if (Math.abs(yvel) > mMinFlingVelocity) {
- if (isDismissable()
- && yvel > 0 && mCollapseOffset > mCollapsibleHeight) {
- smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel);
- mDismissOnScrollerFinished = true;
+ if (getShowAtTop()) {
+ if (isDismissable() && yvel < 0) {
+ abortAnimation();
+ dismiss();
+ } else {
+ smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
+ }
} else {
- smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
+ if (isDismissable()
+ && yvel > 0 && mCollapseOffset > mCollapsibleHeight) {
+ smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel);
+ mDismissOnScrollerFinished = true;
+ } else {
+ smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel);
+ }
}
- } else {
+ }else {
smoothScrollTo(
mCollapseOffset < mCollapsibleHeight / 2 ? 0 : mCollapsibleHeight, 0);
}
@@ -421,6 +448,11 @@
mVelocityTracker.clear();
}
+ private void dismiss() {
+ mRunOnDismissedListener = new RunOnDismissedListener();
+ post(mRunOnDismissedListener);
+ }
+
@Override
public void computeScroll() {
super.computeScroll();
@@ -430,8 +462,7 @@
if (keepGoing) {
postInvalidateOnAnimation();
} else if (mDismissOnScrollerFinished && mOnDismissedListener != null) {
- mRunOnDismissedListener = new RunOnDismissedListener();
- post(mRunOnDismissedListener);
+ dismiss();
}
}
}
@@ -443,6 +474,10 @@
}
private float performDrag(float dy) {
+ if (getShowAtTop()) {
+ return 0;
+ }
+
final float newPos = Math.max(0, Math.min(mCollapseOffset + dy,
mCollapsibleHeight + mUncollapsibleHeight));
if (newPos != mCollapseOffset) {
@@ -656,7 +691,7 @@
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
- if (velocityY > mMinFlingVelocity && mCollapseOffset != 0) {
+ if (!getShowAtTop() && velocityY > mMinFlingVelocity && mCollapseOffset != 0) {
smoothScrollTo(0, velocityY);
return true;
}
@@ -666,12 +701,21 @@
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
if (!consumed && Math.abs(velocityY) > mMinFlingVelocity) {
- if (isDismissable()
- && velocityY < 0 && mCollapseOffset > mCollapsibleHeight) {
- smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, velocityY);
- mDismissOnScrollerFinished = true;
+ if (getShowAtTop()) {
+ if (isDismissable() && velocityY > 0) {
+ abortAnimation();
+ dismiss();
+ } else {
+ smoothScrollTo(velocityY < 0 ? mCollapsibleHeight : 0, velocityY);
+ }
} else {
- smoothScrollTo(velocityY > 0 ? 0 : mCollapsibleHeight, velocityY);
+ if (isDismissable()
+ && velocityY < 0 && mCollapseOffset > mCollapsibleHeight) {
+ smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, velocityY);
+ mDismissOnScrollerFinished = true;
+ } else {
+ smoothScrollTo(velocityY > 0 ? 0 : mCollapsibleHeight, velocityY);
+ }
}
return true;
}
@@ -794,7 +838,11 @@
updateCollapseOffset(oldCollapsibleHeight, !isDragging());
- mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset;
+ if (getShowAtTop()) {
+ mTopOffset = 0;
+ } else {
+ mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset;
+ }
setMeasuredDimension(sourceWidth, heightSize);
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 73a9fbb..8ed958d 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -18,8 +18,6 @@
option java_multiple_files = true;
option java_outer_classname = "IncidentProtoMetadata";
-import "frameworks/base/libs/incident/proto/android/privacy.proto";
-import "frameworks/base/libs/incident/proto/android/section.proto";
import "frameworks/base/core/proto/android/os/cpufreq.proto";
import "frameworks/base/core/proto/android/os/cpuinfo.proto";
import "frameworks/base/core/proto/android/os/incidentheader.proto";
@@ -42,6 +40,8 @@
import "frameworks/base/core/proto/android/service/package.proto";
import "frameworks/base/core/proto/android/service/print.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+import "frameworks/base/libs/incident/proto/android/section.proto";
package android.os;
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index 9945f2e..f684c84 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -18,12 +18,15 @@
option java_multiple_files = true;
option java_outer_classname = "ProcrankProto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/tools/streaming_proto/stream.proto";
package android.os;
//Memory usage of running processes
message Procrank {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Currently running process
repeated ProcessProto processes = 1;
@@ -34,6 +37,7 @@
// Next Tag: 11
message ProcessProto {
option (stream_proto.stream_msg).enable_fields_mapping = true;
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
// ID of the process
optional int32 pid = 1;
@@ -68,6 +72,8 @@
// Next Tag: 3
message SummaryProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional ProcessProto total = 1;
optional ZramProto zram = 2;
@@ -77,9 +83,13 @@
// TODO: sync on how to use these values
message ZramProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string raw_text = 1;
}
message RamProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
optional string raw_text = 1;
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index da8a9cc..3b49d9d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8538,6 +8538,8 @@
<attr name="maxWidth" />
<attr name="maxCollapsedHeight" format="dimension" />
<attr name="maxCollapsedHeightSmall" format="dimension" />
+ <!-- Whether the Drawer should be positioned at the top rather than at the bottom. -->
+ <attr name="showAtTop" format="boolean" />
</declare-styleable>
<declare-styleable name="MessagingLinearLayout">
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 55e804b..9fcb06e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -18,6 +18,7 @@
import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import com.android.internal.widget.ResolverDrawerLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -33,6 +34,8 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.RelativeLayout;
import java.util.ArrayList;
import java.util.List;
@@ -100,6 +103,34 @@
}
@Test
+ public void setShowAtTopToTrue() throws Exception {
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ waitForIdle();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ final View resolverList = activity.findViewById(R.id.resolver_list);
+ final RelativeLayout profileView =
+ (RelativeLayout) activity.findViewById(R.id.profile_button).getParent();
+ assertThat("Drawer should show at bottom by default",
+ profileView.getBottom() == resolverList.getTop() && profileView.getTop() > 0);
+
+ activity.runOnUiThread(() -> {
+ ResolverDrawerLayout layout = (ResolverDrawerLayout)
+ activity.findViewById(
+ R.id.contentPanel);
+ layout.setShowAtTop(true);
+ });
+ waitForIdle();
+ assertThat("Drawer should show at top with new attribute",
+ profileView.getBottom() == resolverList.getTop() && profileView.getTop() == 0);
+ }
+
+ @Test
public void hasLastChosenActivity() throws Exception {
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -283,4 +314,4 @@
private void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
-}
\ No newline at end of file
+}
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index da80989..2849d58 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -29,6 +29,12 @@
using namespace std;
+// DESTINATION enum value, sync with proto/android/privacy.proto
+const uint8_t DEST_LOCAL = 0;
+const uint8_t DEST_EXPLICIT = 100;
+const uint8_t DEST_AUTOMATIC = 200;
+
+
class IncidentReportArgs : public Parcelable {
public:
IncidentReportArgs();
@@ -41,19 +47,19 @@
void setAll(bool all);
void setDest(int dest);
void addSection(int section);
- void addHeader(const vector<int8_t>& header);
+ void addHeader(const vector<uint8_t>& header);
inline bool all() const { return mAll; }
bool containsSection(int section) const;
inline int dest() const { return mDest; }
inline const set<int>& sections() const { return mSections; }
- inline const vector<vector<int8_t>>& headers() const { return mHeaders; }
+ inline const vector<vector<uint8_t>>& headers() const { return mHeaders; }
void merge(const IncidentReportArgs& that);
private:
set<int> mSections;
- vector<vector<int8_t>> mHeaders;
+ vector<vector<uint8_t>> mHeaders;
bool mAll;
int mDest;
};
diff --git a/libs/incident/proto/android/privacy.proto b/libs/incident/proto/android/privacy.proto
index 5fd75d6..7590b22 100644
--- a/libs/incident/proto/android/privacy.proto
+++ b/libs/incident/proto/android/privacy.proto
@@ -34,21 +34,23 @@
// Fields or messages annotated with DEST_EXPLICIT can be sent
// off the device with an explicit user action.
- DEST_EXPLICIT = 1;
+ DEST_EXPLICIT = 100;
// Fields or messages annotated with DEST_AUTOMATIC can be sent by
// automatic means, without per-sending user consent. The user
// still must have previously accepted a consent to share this
// information.
- DEST_AUTOMATIC = 2;
+ DEST_AUTOMATIC = 200;
- // There is no more permissive option than DEST_AUTOMATIC.
+ // This is the default value, which could be overridden by other values.
+ // The reason to pick 255 is it fits into one byte.
+ DEST_UNSET = 255;
+
+ // Currently use 0, 100, 200 and 255, values in between are reserved for futures.
}
message PrivacyFlags {
- optional Destination dest = 1 [
- default = DEST_EXPLICIT
- ];
+ optional Destination dest = 1 [ default = DEST_UNSET ];
// regex to filter pii sensitive info from a string field type
repeated string patterns = 2;
@@ -58,3 +60,8 @@
// Flags for automatically filtering statistics
optional PrivacyFlags privacy = 102672883;
}
+
+extend google.protobuf.MessageOptions {
+ // Flags used to annotate a message which all its unset primitive types inhert this tag.
+ optional PrivacyFlags msg_privacy = 102672883;
+}
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index e628722..bd9c8ee 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -69,7 +69,7 @@
return err;
}
- for (vector<vector<int8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) {
+ for (vector<vector<uint8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) {
err = out->writeByteVector(*it);
if (err != NO_ERROR) {
return err;
@@ -161,7 +161,7 @@
}
void
-IncidentReportArgs::addHeader(const vector<int8_t>& header)
+IncidentReportArgs::addHeader(const vector<uint8_t>& header)
{
mHeaders.push_back(header);
}
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index f20ca43..f4238f2 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -282,6 +282,7 @@
mBoundUserId = UserHandle.USER_NULL;
if (component != null) {
if (D) Log.d(mTag, "unbinding " + component);
+ mBoundService = null;
mContext.unbindService(this);
}
}
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 674bee1..7650795 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -59,24 +59,31 @@
*
* #include "section_list.h"
* ...
- * Privacy WindowState_state { 1, 9, NULL, LOCAL, NULL }; // first two integers are values for field id and proto type.
- * Privacy WindowState_child_windows { 3, 11, NULL, DEFAULT, NULL }; // reserved for WindowState_LIST
- * Privacy* WindowState_MSG_[] = {
+ * Privacy WindowState__state { 1, 9, NULL, LOCAL, NULL }; // first two integers are values for field id and proto type.
+ * Privacy WindowState__child_windows { 3, 11, NULL, UNSET, NULL }; // reserved for WindowState_LIST
+ * Privacy* WindowState__MSG__UNSET[] = {
* &WindowState_state,
* // display id is default, nothing is generated.
* &WindowState_child_windows,
* NULL // terminator of the array
* };
- * Privacy WindowState_my_window { 1, 11, WindowState_my_window_LIST, DEFAULT, NULL };
+ * Privacy WindowState__my_window { 1, 11, WindowState__MSG__UNSET, UNSET, NULL };
*
* createList() {
* ...
- * WindowState_child_windows.children = WindowState_my_window_LIST; // point to its own definition after the list is defined.
+ * WindowState_child_windows.children = WindowState__MSG_UNSET; // point to its own definition after the list is defined.
* ...
* }
*
* const Privacy** PRIVACY_POLICY_LIST = createList();
* const int PRIVACY_POLICY_COUNT = 1;
+ *
+ * Privacy Value Inheritance rules:
+ * 1. Both field and message can be tagged with DESTINATION: LOCAL(L), EXPLICIT(E), AUTOMATIC(A).
+ * 2. Primitives inherits containing message's tag unless defined explicitly.
+ * 3. Containing message's tag doesn't apply to message fields, even when unset (in this case, uses its default message tag).
+ * 4. Message field tag overrides its default message tag.
+ * 5. UNSET tag defaults to EXPLICIT.
*/
// The assignments will be called when constructs PRIVACY_POLICY_LIST, has to be global variable
@@ -164,14 +171,13 @@
return result;
}
-static string getFieldName(const FieldDescriptor* field) {
- return replaceAll(field->full_name(), '.', "__");
+static inline void printPrivacy(const string& name, const FieldDescriptor* field, const string& children,
+ const Destination dest, const string& patterns, const string& comments = "") {
+ printf("Privacy %s = { %d, %d, %s, %d, %s };%s\n", name.c_str(), field->number(), field->type(),
+ children.c_str(), dest, patterns.c_str(), comments.c_str());
}
-static string getMessageTypeName(const Descriptor* descriptor) {
- return replaceAll(descriptor->full_name(), '.', "_") + "_MSG_";
-}
-
+// Get Custom Options ================================================================================
static inline SectionFlags getSectionFlags(const FieldDescriptor* field) {
return field->options().GetExtension(section);
}
@@ -180,24 +186,64 @@
return field->options().GetExtension(privacy);
}
-static inline bool isDefaultField(const FieldDescriptor* field) {
- return getPrivacyFlags(field).dest() == PrivacyFlags::default_instance().dest();
+static inline PrivacyFlags getPrivacyFlags(const Descriptor* descriptor) {
+ return descriptor->options().GetExtension(msg_privacy);
}
-static bool isDefaultMessageImpl(const Descriptor* descriptor, set<string>* parents) {
- int N = descriptor->field_count();
+// Get Destinations ===================================================================================
+static inline Destination getMessageDest(const Descriptor* descriptor, const Destination overridden) {
+ return overridden != DEST_UNSET ? overridden : getPrivacyFlags(descriptor).dest();
+}
+
+// Returns field's own dest, when it is a message field, uses its message default tag if unset.
+static inline Destination getFieldDest(const FieldDescriptor* field) {
+ Destination fieldDest = getPrivacyFlags(field).dest();
+ return field->type() != FieldDescriptor::TYPE_MESSAGE ? fieldDest :
+ getMessageDest(field->message_type(), fieldDest);
+}
+
+// Get Names ===========================================================================================
+static inline string getFieldName(const FieldDescriptor* field) {
+ // replace . with double underscores to avoid name conflicts since fields use snake naming convention
+ return replaceAll(field->full_name(), '.', "__");
+}
+
+
+static inline string getMessageName(const Descriptor* descriptor, const Destination overridden) {
+ // replace . with one underscore since messages use camel naming convention
+ return replaceAll(descriptor->full_name(), '.', "_") + "__MSG__" +
+ to_string(getMessageDest(descriptor, overridden));
+}
+
+// IsDefault ============================================================================================
+// Returns true if a field is default. Default is defined as this field has same dest as its containing message.
+// For message fields, it only looks at its field tag and own default mesaage tag, doesn't recursively go deeper.
+static inline bool isDefaultField(const FieldDescriptor* field, const Destination containerDest) {
+ Destination fieldDest = getFieldDest(field);
+ if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+ return fieldDest == containerDest || (fieldDest == DEST_UNSET);
+ } else {
+ return fieldDest == containerDest ||
+ (containerDest == DEST_UNSET && fieldDest == DEST_EXPLICIT) ||
+ (containerDest == DEST_EXPLICIT && fieldDest == DEST_UNSET);
+ }
+}
+
+static bool isDefaultMessageImpl(const Descriptor* descriptor, const Destination dest, set<string>* parents) {
+ const int N = descriptor->field_count();
+ const Destination messageDest = getMessageDest(descriptor, dest);
parents->insert(descriptor->full_name());
for (int i=0; i<N; ++i) {
const FieldDescriptor* field = descriptor->field(i);
- // look at if the current field is default or not, return false immediately
- if (!isDefaultField(field)) return false;
-
+ const Destination fieldDest = getFieldDest(field);
+ // If current field is not default, return false immediately
+ if (!isDefaultField(field, messageDest)) return false;
switch (field->type()) {
case FieldDescriptor::TYPE_MESSAGE:
// if self recursion, don't go deep.
if (parents->find(field->message_type()->full_name()) != parents->end()) break;
// if is a default message, just continue
- if (isDefaultMessageImpl(field->message_type(), parents)) break;
+ if (isDefaultMessageImpl(field->message_type(), fieldDest, parents)) break;
// sub message is not default, so this message is always not default
return false;
case FieldDescriptor::TYPE_STRING:
@@ -210,106 +256,118 @@
return true;
}
-static bool isDefaultMessage(const Descriptor* descriptor) {
+// Recursively look at if this message is default, meaning all its fields and sub-messages
+// can be described by the same dest.
+static bool isDefaultMessage(const Descriptor* descriptor, const Destination dest) {
set<string> parents;
- return isDefaultMessageImpl(descriptor, &parents);
+ return isDefaultMessageImpl(descriptor, dest, &parents);
}
-// This function is called for looking at privacy tags for a message type and recursively its sub-messages
-// It prints out each fields's privacy tags and a List of Privacy of the message itself (don't print default values)
-// Returns false if the descriptor doesn't have any non default privacy flags set, including its submessages
-static bool generatePrivacyFlags(const Descriptor* descriptor, map<string, bool> &msgNames, set<string>* parents) {
- bool hasDefaultFlags[descriptor->field_count()];
+// ===============================================================================================================
+static bool numberInOrder(const FieldDescriptor* f1, const FieldDescriptor* f2) {
+ return f1->number() < f2->number();
+}
- string messageTypeName = getMessageTypeName(descriptor);
- // if the message is already defined, skip it.
- if (msgNames.find(messageTypeName) != msgNames.end()) {
- bool hasDefault = msgNames[messageTypeName];
- return !hasDefault; // don't generate if it has default privacy.
+// field numbers are possibly out of order, sort them here.
+static vector<const FieldDescriptor*> sortFields(const Descriptor* descriptor) {
+ vector<const FieldDescriptor*> fields;
+ fields.reserve(descriptor->field_count());
+ for (int i=0; i<descriptor->field_count(); i++) {
+ fields.push_back(descriptor->field(i));
+ }
+ std::sort(fields.begin(), fields.end(), numberInOrder);
+ return fields;
+}
+
+// This function looks for privacy tags of a message type and recursively its sub-messages.
+// It generates Privacy objects for each non-default fields including non-default sub-messages.
+// And if the message has Privacy objects generated, it returns a list of them.
+// Returns false if the descriptor doesn't have any non default privacy flags set, including its submessages
+static bool generatePrivacyFlags(const Descriptor* descriptor, const Destination overridden,
+ map<string, bool> &variableNames, set<string>* parents) {
+ const string messageName = getMessageName(descriptor, overridden);
+ const Destination messageDest = getMessageDest(descriptor, overridden);
+
+ if (variableNames.find(messageName) != variableNames.end()) {
+ bool hasDefault = variableNames[messageName];
+ return !hasDefault; // if has default, then don't generate privacy flags.
}
// insert the message type name so sub-message will figure out if self-recursion occurs
- parents->insert(messageTypeName);
+ parents->insert(messageName);
- // iterate though its field and generate sub flags first
- for (int i=0; i<descriptor->field_count(); i++) {
- hasDefaultFlags[i] = true; // set default to true
-
- const FieldDescriptor* field = descriptor->field(i);
+ // sort fields based on number, iterate though them and generate sub flags first
+ vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
+ bool hasDefaultFlags[fieldsInOrder.size()];
+ for (size_t i=0; i<fieldsInOrder.size(); i++) {
+ const FieldDescriptor* field = fieldsInOrder[i];
const string fieldName = getFieldName(field);
- // check if the same field name is already defined.
- if (msgNames.find(fieldName) != msgNames.end()) {
- hasDefaultFlags[i] = msgNames[fieldName];
- continue;
- };
+ const Destination fieldDest = getFieldDest(field);
- PrivacyFlags p = getPrivacyFlags(field);
+ if (variableNames.find(fieldName) != variableNames.end()) {
+ hasDefaultFlags[i] = variableNames[fieldName];
+ continue;
+ }
+ hasDefaultFlags[i] = isDefaultField(field, messageDest);
+
string fieldMessageName;
+ PrivacyFlags p = getPrivacyFlags(field);
switch (field->type()) {
case FieldDescriptor::TYPE_MESSAGE:
- fieldMessageName = getMessageTypeName(field->message_type());
+ fieldMessageName = getMessageName(field->message_type(), fieldDest);
if (parents->find(fieldMessageName) != parents->end()) { // Self-Recursion proto definition
- if (isDefaultField(field)) {
- hasDefaultFlags[i] = isDefaultMessage(field->message_type());
- } else {
- hasDefaultFlags[i] = false;
+ if (hasDefaultFlags[i]) {
+ hasDefaultFlags[i] = isDefaultMessage(field->message_type(), fieldDest);
}
if (!hasDefaultFlags[i]) {
- printf("Privacy %s = { %d, %d, NULL, %d, NULL }; // self recursion field of %s\n",
- fieldName.c_str(), field->number(), field->type(), p.dest(), fieldMessageName.c_str());
+ printPrivacy(fieldName, field, "NULL", fieldDest, "NULL",
+ " // self recursion field of " + fieldMessageName);
// generate the assignment and used to construct createList function later on.
gSelfRecursionAssignments.push_back(fieldName + ".children = " + fieldMessageName);
}
- break;
- } else if (generatePrivacyFlags(field->message_type(), msgNames, parents)) {
- printf("Privacy %s = { %d, %d, %s, %d, NULL };\n", fieldName.c_str(), field->number(),
- field->type(), fieldMessageName.c_str(), p.dest());
- } else if (isDefaultField(field)) {
- // don't create a new privacy if the value is default.
- break;
- } else {
- printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
- field->type(), p.dest());
+ } else if (generatePrivacyFlags(field->message_type(), p.dest(), variableNames, parents)) {
+ if (variableNames.find(fieldName) == variableNames.end()) {
+ printPrivacy(fieldName, field, fieldMessageName, fieldDest, "NULL");
+ }
+ hasDefaultFlags[i] = false;
+ } else if (!hasDefaultFlags[i]) {
+ printPrivacy(fieldName, field, "NULL", fieldDest, "NULL");
}
- hasDefaultFlags[i] = false;
break;
case FieldDescriptor::TYPE_STRING:
- if (isDefaultField(field) && p.patterns_size() == 0) break;
-
- printf("const char* %s_patterns[] = {\n", fieldName.c_str());
- for (int i=0; i<p.patterns_size(); i++) {
- // the generated string need to escape backslash as well, need to dup it here
- printf(" \"%s\",\n", replaceAll(p.patterns(i), '\\', "\\\\").c_str());
+ if (p.patterns_size() != 0) { // if patterns are specified
+ if (hasDefaultFlags[i]) break;
+ printf("const char* %s_patterns[] = {\n", fieldName.c_str());
+ for (int j=0; j<p.patterns_size(); j++) {
+ // generated string needs to escape backslash too, duplicate it to allow escape again.
+ printf(" \"%s\",\n", replaceAll(p.patterns(j), '\\', "\\\\").c_str());
+ }
+ printf(" NULL };\n");
+ printPrivacy(fieldName, field, "NULL", fieldDest, fieldName + "_patterns");
+ break;
}
- printf(" NULL };\n");
- printf("Privacy %s = { %d, %d, NULL, %d, %s_patterns };\n", fieldName.c_str(), field->number(),
- field->type(), p.dest(), fieldName.c_str());
- hasDefaultFlags[i] = false;
- break;
+ // else treat string field as primitive field and goes to default
default:
- if (isDefaultField(field)) break;
- printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
- field->type(), p.dest());
- hasDefaultFlags[i] = false;
+ if (!hasDefaultFlags[i]) printPrivacy(fieldName, field, "NULL", fieldDest, "NULL");
}
- // add the field name to message map, true means it has default flags
- msgNames[fieldName] = hasDefaultFlags[i];
+ // Don't generate a variable twice
+ if (!hasDefaultFlags[i]) variableNames[fieldName] = false;
}
bool allDefaults = true;
- for (int i=0; i<descriptor->field_count(); i++) {
+ for (size_t i=0; i<fieldsInOrder.size(); i++) {
allDefaults &= hasDefaultFlags[i];
}
- parents->erase(messageTypeName); // erase the message type name when exit the message.
- msgNames[messageTypeName] = allDefaults; // store the privacy tags of the message here to avoid overhead.
+ parents->erase(messageName); // erase the message type name when exit the message.
+ variableNames[messageName] = allDefaults; // store the privacy tags of the message here to avoid overhead.
if (allDefaults) return false;
emptyline();
int policyCount = 0;
- printf("Privacy* %s[] = {\n", messageTypeName.c_str());
- for (int i=0; i<descriptor->field_count(); i++) {
- const FieldDescriptor* field = descriptor->field(i);
+ printf("Privacy* %s[] = {\n", messageName.c_str());
+ for (size_t i=0; i<fieldsInOrder.size(); i++) {
+ const FieldDescriptor* field = fieldsInOrder[i];
if (hasDefaultFlags[i]) continue;
printf(" &%s,\n", getFieldName(field).c_str());
policyCount++;
@@ -359,29 +417,30 @@
// generates PRIVACY_POLICY_LIST
printf("// Generate PRIVACY_POLICY_LIST.\n\n");
- map<string, bool> messageNames;
+ map<string, bool> variableNames;
set<string> parents;
- bool skip[descriptor->field_count()];
+ vector<const FieldDescriptor*> fieldsInOrder = sortFields(descriptor);
+ bool skip[fieldsInOrder.size()];
+ const Destination incidentDest = getPrivacyFlags(descriptor).dest();
- for (int i=0; i<descriptor->field_count(); i++) {
- const FieldDescriptor* field = descriptor->field(i);
+ for (size_t i=0; i<fieldsInOrder.size(); i++) {
+ const FieldDescriptor* field = fieldsInOrder[i];
const string fieldName = getFieldName(field);
- PrivacyFlags p = getPrivacyFlags(field);
+ const Destination fieldDest = getFieldDest(field);
+ const string fieldMessageName = getMessageName(field->message_type(), fieldDest);
skip[i] = true;
if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
continue;
}
- // generate privacy flags for each field.
- if (generatePrivacyFlags(field->message_type(), messageNames, &parents)) {
- printf("Privacy %s { %d, %d, %s, %d, NULL };\n", fieldName.c_str(), field->number(),
- field->type(), getMessageTypeName(field->message_type()).c_str(), p.dest());
- } else if (isDefaultField(field)) {
+ // generate privacy flags for each section.
+ if (generatePrivacyFlags(field->message_type(), fieldDest, variableNames, &parents)) {
+ printPrivacy(fieldName, field, fieldMessageName, fieldDest, "NULL");
+ } else if (isDefaultField(field, incidentDest)) {
continue; // don't create a new privacy if the value is default.
} else {
- printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
- field->type(), p.dest());
+ printPrivacy(fieldName, field, "NULL", fieldDest, "NULL");
}
skip[i] = false;
}
@@ -391,16 +450,16 @@
int policyCount = 0;
if (gSelfRecursionAssignments.empty()) {
printf("Privacy* privacyArray[] = {\n");
- for (int i=0; i<descriptor->field_count(); i++) {
+ for (size_t i=0; i<fieldsInOrder.size(); i++) {
if (skip[i]) continue;
- printf(" &%s,\n", getFieldName(descriptor->field(i)).c_str());
+ printf(" &%s,\n", getFieldName(fieldsInOrder[i]).c_str());
policyCount++;
}
printf("};\n\n");
printf("const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(privacyArray);\n\n");
printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount);
} else {
- for (int i=0; i<descriptor->field_count(); i++) {
+ for (size_t i=0; i<fieldsInOrder.size(); i++) {
if (!skip[i]) policyCount++;
}
@@ -410,9 +469,9 @@
}
printf(" Privacy** privacyArray = (Privacy**)malloc(%d * sizeof(Privacy**));\n", policyCount);
policyCount = 0; // reset
- for (int i=0; i<descriptor->field_count(); i++) {
+ for (size_t i=0; i<fieldsInOrder.size(); i++) {
if (skip[i]) continue;
- printf(" privacyArray[%d] = &%s;\n", policyCount++, getFieldName(descriptor->field(i)).c_str());
+ printf(" privacyArray[%d] = &%s;\n", policyCount++, getFieldName(fieldsInOrder[i]).c_str());
}
printf(" return const_cast<const Privacy**>(privacyArray);\n");
printf("}\n\n");