/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #ifndef FALLTHROUGH_INTENDED #define FALLTHROUGH_INTENDED [[fallthrough]] #endif using namespace android; using namespace android::os; using namespace google::protobuf; 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, 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__MSG__UNSET, UNSET, NULL }; * * createList() { * ... * 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 vector gSelfRecursionAssignments; static inline void emptyline() { printf("\n"); } static void generateHead(const char* header) { printf("// Auto generated file. Do not modify\n"); emptyline(); printf("#include \"%s.h\"\n", header); emptyline(); } // ======================== incident_sections ============================= static bool generateIncidentSectionsCpp(Descriptor const* descriptor) { generateHead("incident_sections"); map sections; int N; N = descriptor->field_count(); for (int i=0; ifield(i); sections[field->name()] = field; } printf("IncidentSection const INCIDENT_SECTIONS[] = {\n"); N = sections.size(); int i = 0; for (map::const_iterator it = sections.begin(); it != sections.end(); it++, i++) { const FieldDescriptor* field = it->second; printf(" { %d, \"%s\" }", field->number(), field->name().c_str()); if (i != N-1) { printf(",\n"); } else { printf("\n"); } } printf("};\n"); printf("const int INCIDENT_SECTION_COUNT = %d;\n", N); return true; } // ========================= section_list =================================== static void splitAndPrint(const string& args) { size_t base = 0; size_t found; while (true) { found = args.find_first_of(' ', base); if (found != base) { string arg = args.substr(base, found - base); printf(" \"%s\",", arg.c_str()); } if (found == args.npos) break; base = found + 1; } } 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[fieldName.size() * newS.size() + 1]; // assign a larger buffer while (pos != fieldName.size()) { char cur = fieldName[pos++]; if (cur != oldC) { res[idx++] = cur; continue; } for (size_t i=0; inumber(), field->type(), children.c_str(), dest, patterns.c_str(), comments.c_str()); } // Get Custom Options ================================================================================ 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 PrivacyFlags getPrivacyFlags(const Descriptor* descriptor) { return descriptor->options().GetExtension(msg_privacy); } // 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); } // Converts Destination to a string. static inline string getDestString(const Destination dest) { switch (dest) { case DEST_AUTOMATIC: return "AUTOMATIC"; case DEST_LOCAL: return "LOCAL"; case DEST_EXPLICIT: return "EXPLICIT"; // UNSET is considered EXPLICIT by default. case DEST_UNSET: return "EXPLICIT"; default: return "UNKNOWN"; } } // 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 message 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* parents) { const int N = descriptor->field_count(); const Destination messageDest = getMessageDest(descriptor, dest); parents->insert(descriptor->full_name()); for (int i=0; ifield(i); 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(), fieldDest, 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; break; default: break; } } parents->erase(descriptor->full_name()); return true; } // 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 parents; return isDefaultMessageImpl(descriptor, dest, &parents); } // =============================================================================================================== static bool numberInOrder(const FieldDescriptor* f1, const FieldDescriptor* f2) { return f1->number() < f2->number(); } // field numbers are possibly out of order, sort them here. static vector sortFields(const Descriptor* descriptor) { vector fields; fields.reserve(descriptor->field_count()); for (int i=0; ifield_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 &variableNames, set* 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(messageName); // sort fields based on number, iterate though them and generate sub flags first vector fieldsInOrder = sortFields(descriptor); bool hasDefaultFlags[fieldsInOrder.size()]; for (size_t i=0; itype()) { case FieldDescriptor::TYPE_MESSAGE: fieldMessageName = getMessageName(field->message_type(), fieldDest); if (parents->find(fieldMessageName) != parents->end()) { // Self-Recursion proto definition if (hasDefaultFlags[i]) { hasDefaultFlags[i] = isDefaultMessage(field->message_type(), fieldDest); } if (!hasDefaultFlags[i]) { 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); } } 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"); } break; case FieldDescriptor::TYPE_STRING: 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; jerase(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", messageName.c_str()); for (size_t i=0; ifield_count(); i++) { const FieldDescriptor* field = descriptor->field(i); if (field->type() != FieldDescriptor::TYPE_MESSAGE && field->type() != FieldDescriptor::TYPE_STRING && field->type() != FieldDescriptor::TYPE_BYTES) { continue; } const SectionFlags s = getSectionFlags(field); if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) { printf("#if ALLOW_RESTRICTED_SECTIONS\n"); } switch (s.type()) { case SECTION_NONE: continue; case SECTION_FILE: printf(" new FileSection(%d, \"%s\"),\n", field->number(), s.args().c_str()); break; case SECTION_COMMAND: printf(" new CommandSection(%d,", field->number()); splitAndPrint(s.args()); printf(" NULL),\n"); break; case SECTION_DUMPSYS: printf(" new DumpsysSection(%d, ", field->number()); splitAndPrint(s.args()); printf(" NULL),\n"); break; case SECTION_LOG: printf(" new LogSection(%d, ", field->number()); splitAndPrint(s.args()); printf(" NULL),\n"); break; case SECTION_GZIP: printf(" new GZipSection(%d,", field->number()); splitAndPrint(s.args()); printf(" NULL),\n"); break; case SECTION_TOMBSTONE: printf(" new TombstoneSection(%d, \"%s\"),\n", field->number(), s.args().c_str()); break; case SECTION_TEXT_DUMPSYS: printf(" new TextDumpsysSection(%d, ", field->number()); splitAndPrint(s.args()); printf(" NULL),\n"); break; } if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) { printf("#endif\n"); } } printf(" NULL };\n"); emptyline(); printf("// =============================================================================\n"); emptyline(); // generates PRIVACY_POLICY_LIST printf("// Generate PRIVACY_POLICY_LIST.\n\n"); map variableNames; set parents; vector fieldsInOrder = sortFields(descriptor); vector skip(fieldsInOrder.size()); const Destination incidentDest = getPrivacyFlags(descriptor).dest(); for (size_t i=0; iname().c_str(), field->number()); if (field->type() != FieldDescriptor::TYPE_MESSAGE) { printPrivacy(fieldName, field, "NULL", fieldDest, "NULL"); continue; } skip[i] = true; const string fieldMessageName = getMessageName(field->message_type(), fieldDest); // generate privacy flags for each section. if (generatePrivacyFlags(field->message_type(), incidentDest, variableNames, &parents)) { printPrivacy(fieldName, field, fieldMessageName, fieldDest, "NULL"); } else if (fieldDest == incidentDest) { printf("// default %s: fieldDest=%d incidentDest=%d\n", fieldName.c_str(), getFieldDest(field), incidentDest); continue; // don't create a new privacy if the value is default. } else { printPrivacy(fieldName, field, "NULL", fieldDest, "NULL"); } skip[i] = false; } // generate final PRIVACY_POLICY_LIST emptyline(); int policyCount = 0; if (gSelfRecursionAssignments.empty()) { printf("Privacy* privacyArray[] = {\n"); for (size_t i=0; i(privacyArray);\n\n"); printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount); } else { for (size_t i=0; i(privacyArray);\n"); printf("}\n\n"); printf("const Privacy** PRIVACY_POLICY_LIST = createList();\n\n"); printf("const int PRIVACY_POLICY_COUNT = %d;\n", policyCount); } printf("} // incidentd\n"); printf("} // os\n"); printf("} // android\n"); return true; } // ================================================================================ static string replace_string(const string& str, const char replace, const char with) { string result(str); const int N = result.size(); for (int i=0; i* parents, const Destination containerDest = DEST_UNSET) { DebugStringOptions options; options.include_comments = true; for (int i=0; ifield_count(); i++) { const FieldDescriptor* field = descriptor->field(i); const Destination fieldDest = getFieldDest(field); stringstream text; if (field->type() == FieldDescriptor::TYPE_MESSAGE) { text << field->message_type()->name(); } else { text << field->type_name(); } text << " " << field->name(); text << " (PRIVACY="; if (isDefaultField(field, containerDest)) { text << getDestString(containerDest); } else { text << getDestString(fieldDest); } text << ")"; printf("%s%s,\n", indent.c_str(), replace_string(text.str(), '\n', ' ').c_str()); if (field->type() == FieldDescriptor::TYPE_MESSAGE && parents->find(field->message_type()->full_name()) == parents->end()) { parents->insert(field->message_type()->full_name()); generateCsv(field->message_type(), indent + ",", parents, fieldDest); parents->erase(field->message_type()->full_name()); } } } // ================================================================================ int main(int argc, char const *argv[]) { if (argc < 2) return 1; const char* module = argv[1]; Descriptor const* descriptor = IncidentProto::descriptor(); if (strcmp(module, "incident") == 0) { return !generateIncidentSectionsCpp(descriptor); } if (strcmp(module, "incidentd") == 0 ) { return !generateSectionListCpp(descriptor); } // Generates Csv Format of proto definition for each section. if (strcmp(module, "csv") == 0 && argc > 2) { int sectionId = atoi(argv[2]); for (int i=0; ifield_count(); i++) { const FieldDescriptor* field = descriptor->field(i); if (strcmp(field->name().c_str(), argv[2]) == 0 || field->number() == sectionId) { set parents; printf("%s\n", field->name().c_str()); generateCsv(field->message_type(), "", &parents, getFieldDest(field)); break; } } // Returns failure if csv is enabled to prevent Android building with it. // It doesn't matter if this command runs manually. return 1; } // Returns failure if not called by the whitelisted modules return 1; }