OSDN Git Service

Enable Window Dumpsys Section
authorYi Jin <jinyithu@google.com>
Tue, 24 Oct 2017 19:30:24 +0000 (12:30 -0700)
committerYi Jin <jinyithu@google.com>
Thu, 2 Nov 2017 01:24:57 +0000 (18:24 -0700)
Protobuf defines classes, which can have self recursive message
definitions and cause a bug in generating privacy flags.
Solve the problem here. The details is in incident_section_gen/main.cpp.
The logic is a bit complicated to address more than one level of self
recursion proto message definition.

Also solve a bug when PrivacyBuffer strips fields.
Modify PRIVACY_POLICY_LIST to be type Privacy** in order to allow
initialization by a method.

Bug: 68162512
Test: unit tested and on device tests
Change-Id: I1d0b79f6813e5fd66c4cf5823d0fa17efc57bb1d

cmds/incidentd/src/PrivacyBuffer.cpp
cmds/incidentd/src/section_list.h
cmds/incidentd/tests/PrivacyBuffer_test.cpp
cmds/incidentd/tests/section_list.cpp
core/proto/android/os/incident.proto
core/proto/android/server/windowmanagerservice.proto
tools/incident_section_gen/main.cpp

index d926ea7..77ae1a7 100644 (file)
@@ -87,12 +87,12 @@ PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec)
     // 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());
     while (mData.rp()->pos() - start.pos() != msgSize) {
-        long long token = mProto.start(policy->EncodedFieldId());
         status_t err = stripField(policy, spec);
         if (err != NO_ERROR) return err;
-        mProto.end(token);
     }
+    mProto.end(token);
     return NO_ERROR;
 }
 
index da82b00..dfd2312 100644 (file)
@@ -30,7 +30,7 @@ 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, not NULL-terminated since size is provided.
  */
-extern const Privacy* PRIVACY_POLICY_LIST[];
+extern const Privacy** PRIVACY_POLICY_LIST;
 
 extern const int PRIVACY_POLICY_COUNT;
 
index 8f6e355..84a2a82 100644 (file)
@@ -260,3 +260,13 @@ TEST_F(PrivacyBufferTest, BadDataInNestedMessage) {
     PrivacySpec spec;
     ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE);
 }
+
+TEST_F(PrivacyBufferTest, SelfRecursionMessage) {
+    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 };
+    field5->children = list;
+    string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2;
+    assertStrip(EXPLICIT, expected, field5);
+}
index e47b61c..4acc429 100644 (file)
@@ -20,9 +20,11 @@ Privacy* list[] = {
 Privacy field_0 { 0, 11, list, EXPLICIT, NULL };
 Privacy field_1 { 1, 9, NULL, AUTOMATIC, NULL };
 
-const Privacy* PRIVACY_POLICY_LIST[] = {
+Privacy* final_list[] = {
     &field_0,
     &field_1
 };
 
-const int PRIVACY_POLICY_COUNT = 2;
\ No newline at end of file
+const Privacy** PRIVACY_POLICY_LIST = const_cast<const Privacy**>(final_list);
+
+const int PRIVACY_POLICY_COUNT = 2;
index 50e811d..e998b09 100644 (file)
@@ -27,6 +27,7 @@ import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
 import "frameworks/base/core/proto/android/os/procrank.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
 import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
+import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 import "frameworks/base/core/proto/android/server/fingerprint.proto";
 import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
 import "frameworks/base/core/proto/android/service/appwidget.proto";
@@ -135,4 +136,9 @@ message IncidentProto {
         (section).type = SECTION_DUMPSYS,
         (section).args = "alarm --proto"
     ];
+
+    optional com.android.server.wm.proto.WindowManagerServiceProto window = 3017 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "window --proto"
+    ];
 }
index 064523a..4d48a42 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 syntax = "proto2";
+
 import "frameworks/base/core/proto/android/content/configuration.proto";
 import "frameworks/base/core/proto/android/graphics/rect.proto";
 import "frameworks/base/core/proto/android/view/displayinfo.proto";
index 135df40..674bee1 100644 (file)
@@ -18,6 +18,7 @@
 #include <frameworks/base/core/proto/android/os/incident.pb.h>
 
 #include <map>
+#include <set>
 #include <string>
 
 using namespace android;
@@ -27,6 +28,60 @@ using namespace google::protobuf::io;
 using namespace google::protobuf::internal;
 using namespace std;
 
+/**
+ * Implementation details:
+ * This binary auto generates .cpp files for incident and incidentd.
+ *
+ * When argument "incident" is specified, it generates incident_section.cpp file.
+ *
+ * When argument "incidentd" is specified, it generates section_list.cpp file.
+ *
+ * In section_list.cpp file, it generates a SECTION_LIST array and a PRIVACY_POLICY_LIST array.
+ * For SECTION_LIST, it generates Section.h classes only for proto fields with section option enabled.
+ * For PRIVACY_POLICY_LIST, it generates Privacy.h classes only for proto fields with privacy option enabled.
+ *
+ * For Privacy struct, it is possible to have self recursion definitions since protobuf is defining "classes"
+ * So the logic to handle it becomes very complicated when Privacy tag of a message contains a list of Privacies
+ * of its sub-messages. The code also handles multiple depth of self recursion fields.
+ *
+ * For example here is a one level self recursion message WindowManager:
+ * message WindowState {
+ *     string state = 1 [(privacy).dest = LOCAL];
+ *     int32  display_id = 2;
+ *     repeated WindowState child_windows = 3;
+ * }
+ *
+ * message WindowManager {
+ *     WindowState my_window = 1;
+ * }
+ *
+ * When generating Privacy options for WindowManager, this tool will generate cpp syntax source code:
+ *
+ * #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_[] = {
+ *     &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 };
+ *
+ * createList() {
+ *    ...
+ *    WindowState_child_windows.children = WindowState_my_window_LIST; // point to its own definition after the list is defined.
+ *    ...
+ * }
+ *
+ * const Privacy** PRIVACY_POLICY_LIST = createList();
+ * const int PRIVACY_POLICY_COUNT = 1;
+ */
+
+// The assignments will be called when constructs PRIVACY_POLICY_LIST, has to be global variable
+vector<string> gSelfRecursionAssignments;
+
 static inline void emptyline() {
     printf("\n");
 }
@@ -38,7 +93,7 @@ static void generateHead(const char* header) {
     emptyline();
 }
 
-// ================================================================================
+// ======================== incident_sections =============================
 static bool generateIncidentSectionsCpp(Descriptor const* descriptor)
 {
     generateHead("incident_sections");
@@ -73,7 +128,7 @@ static bool generateIncidentSectionsCpp(Descriptor const* descriptor)
     return true;
 }
 
-// ================================================================================
+// ========================= section_list ===================================
 static void splitAndPrint(const string& args) {
     size_t base = 0;
     size_t found;
@@ -88,12 +143,12 @@ static void splitAndPrint(const string& args) {
     }
 }
 
-static const std::string replaceAll(const string& field_name, const char oldC, const string& newS) {
-    if (field_name.find_first_of(oldC) == field_name.npos) return field_name.c_str();
+static string replaceAll(const string& fieldName, const char oldC, const string& newS) {
+    if (fieldName.find_first_of(oldC) == fieldName.npos) return fieldName.c_str();
     size_t pos = 0, idx = 0;
-    char* res = new char[field_name.size() * newS.size() + 1]; // assign a larger buffer
-    while (pos != field_name.size()) {
-        char cur = field_name[pos++];
+    char* res = new char[fieldName.size() * newS.size() + 1]; // assign a larger buffer
+    while (pos != fieldName.size()) {
+        char cur = fieldName[pos++];
         if (cur != oldC) {
             res[idx++] = cur;
             continue;
@@ -104,92 +159,162 @@ static const std::string replaceAll(const string& field_name, const char oldC, c
         }
     }
     res[idx] = '\0';
-    std::string result(res);
+    string result(res);
     delete [] res;
     return result;
 }
 
-static inline bool isDefaultDest(const FieldDescriptor* field) {
-    return field->options().GetExtension(privacy).dest() == PrivacyFlags::default_instance().dest();
+static string getFieldName(const FieldDescriptor* field) {
+    return replaceAll(field->full_name(), '.', "__");
+}
+
+static string getMessageTypeName(const Descriptor* descriptor) {
+    return replaceAll(descriptor->full_name(), '.', "_") + "_MSG_";
+}
+
+static inline SectionFlags getSectionFlags(const FieldDescriptor* field) {
+    return field->options().GetExtension(section);
 }
 
+static inline PrivacyFlags getPrivacyFlags(const FieldDescriptor* field) {
+    return field->options().GetExtension(privacy);
+}
+
+static inline bool isDefaultField(const FieldDescriptor* field) {
+    return getPrivacyFlags(field).dest() == PrivacyFlags::default_instance().dest();
+}
+
+static bool isDefaultMessageImpl(const Descriptor* descriptor, set<string>* parents) {
+    int N = descriptor->field_count();
+    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;
+
+        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;
+                // sub message is not default, so this message is always not default
+                return false;
+            case FieldDescriptor::TYPE_STRING:
+                if (getPrivacyFlags(field).patterns_size() != 0) return false;
+            default:
+                continue;
+        }
+    }
+    parents->erase(descriptor->full_name());
+    return true;
+}
+
+static bool isDefaultMessage(const Descriptor* descriptor) {
+    set<string> parents;
+    return isDefaultMessageImpl(descriptor, &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, const char* alias, map<string, bool> &msgNames) {
+static bool generatePrivacyFlags(const Descriptor* descriptor, map<string, bool> &msgNames, set<string>* parents) {
     bool hasDefaultFlags[descriptor->field_count()];
+
+    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.
+    }
+    // insert the message type name so sub-message will figure out if self-recursion occurs
+    parents->insert(messageTypeName);
+
     // 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);
-        const std::string field_name_str = replaceAll(field->full_name(), '.', "__");
-        const char* field_name = field_name_str.c_str();
-        // check if the same name is already defined
-        if (msgNames.find(field_name) != msgNames.end()) {
-            hasDefaultFlags[i] = msgNames[field_name];
+        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;
         };
 
-        PrivacyFlags p = field->options().GetExtension(privacy);
+        PrivacyFlags p = getPrivacyFlags(field);
+        string fieldMessageName;
         switch (field->type()) {
             case FieldDescriptor::TYPE_MESSAGE:
-                if (generatePrivacyFlags(field->message_type(), field_name, msgNames)) {
-                    printf("Privacy %s { %d, %d, %s_LIST, %d, NULL };\n", field_name, field->number(),
-                            field->type(), field_name, p.dest());
-                } else if (isDefaultDest(field)) {
+                fieldMessageName = getMessageTypeName(field->message_type());
+                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]) {
+                        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());
+                        // 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", field_name, field->number(),
+                } else {
+                    printf("Privacy %s = { %d, %d, NULL, %d, NULL };\n", fieldName.c_str(), field->number(),
                             field->type(), p.dest());
                 }
                 hasDefaultFlags[i] = false;
                 break;
             case FieldDescriptor::TYPE_STRING:
-                if (isDefaultDest(field) && p.patterns_size() == 0) break;
+                if (isDefaultField(field) && p.patterns_size() == 0) break;
 
-                printf("const char* %s_patterns[] = {\n", field_name);
+                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());
                 }
                 printf("    NULL };\n");
-                printf("Privacy %s { %d, %d, NULL, %d, %s_patterns };\n", field_name, field->number(),
-                        field->type(), p.dest(), field_name);
+                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;
             default:
-                if (isDefaultDest(field)) break;
-                printf("Privacy %s { %d, %d, NULL, %d, NULL };\n", field_name, field->number(),
+                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;
         }
         // add the field name to message map, true means it has default flags
-        msgNames[field_name] = hasDefaultFlags[i];
+        msgNames[fieldName] = hasDefaultFlags[i];
     }
 
     bool allDefaults = true;
     for (int i=0; i<descriptor->field_count(); 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.
+
     if (allDefaults) return false;
 
     emptyline();
-
-    bool needConst = strcmp(alias, "PRIVACY_POLICY") == 0;
     int policyCount = 0;
-
-    printf("%sPrivacy* %s_LIST[] = {\n", needConst ? "const " : "", alias);
+    printf("Privacy* %s[] = {\n", messageTypeName.c_str());
     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());
+        printf("    &%s,\n", getFieldName(field).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 true;
 }
@@ -198,6 +323,8 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
     generateHead("section_list");
 
     // generates SECTION_LIST
+    printf("// Generate SECTION_LIST.\n\n");
+
     printf("const Section* SECTION_LIST[] = {\n");
     for (int i=0; i<descriptor->field_count(); i++) {
         const FieldDescriptor* field = descriptor->field(i);
@@ -205,7 +332,7 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
         if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
             continue;
         }
-        const SectionFlags s = field->options().GetExtension(section);
+        const SectionFlags s = getSectionFlags(field);
         switch (s.type()) {
             case SECTION_NONE:
                 continue;
@@ -225,16 +352,73 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
         }
     }
     printf("    NULL };\n");
+
+    emptyline();
+    printf("// =============================================================================\n");
     emptyline();
 
-    // generates PRIVACY_POLICY
+    // generates PRIVACY_POLICY_LIST
+    printf("// Generate PRIVACY_POLICY_LIST.\n\n");
     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[] = {};\n");
-        printf("const int PRIVACY_POLICY_COUNT = 0;\n");
+    set<string> parents;
+    bool skip[descriptor->field_count()];
+
+    for (int i=0; i<descriptor->field_count(); i++) {
+        const FieldDescriptor* field = descriptor->field(i);
+        const string fieldName = getFieldName(field);
+        PrivacyFlags p = getPrivacyFlags(field);
+
+        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)) {
+            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());
+        }
+        skip[i] = false;
     }
 
+    // generate final PRIVACY_POLICY_LIST
+    emptyline();
+    int policyCount = 0;
+    if (gSelfRecursionAssignments.empty()) {
+        printf("Privacy* privacyArray[] = {\n");
+        for (int i=0; i<descriptor->field_count(); i++) {
+            if (skip[i]) continue;
+            printf("    &%s,\n", getFieldName(descriptor->field(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++) {
+            if (!skip[i]) policyCount++;
+        }
+
+        printf("static const Privacy** createList() {\n");
+        for (size_t i=0; i<gSelfRecursionAssignments.size(); ++i) {
+            printf("    %s;\n", gSelfRecursionAssignments[i].c_str());
+        }
+        printf("    Privacy** privacyArray = (Privacy**)malloc(%d * sizeof(Privacy**));\n", policyCount);
+        policyCount = 0; // reset
+        for (int i=0; i<descriptor->field_count(); i++) {
+            if (skip[i]) continue;
+            printf("    privacyArray[%d] = &%s;\n", policyCount++, getFieldName(descriptor->field(i)).c_str());
+        }
+        printf("    return const_cast<const Privacy**>(privacyArray);\n");
+        printf("}\n\n");
+        printf("const Privacy** PRIVACY_POLICY_LIST = createList();\n\n");
+        printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount);
+    }
     return true;
 }