Add proto dump flag to services (1/2)

Adds new PROTO flag which requests services to dump sections
in proto format. Modifies dumpsys to take in proto argument
and pass on proto flags to services which support proto
dumps. Modify PriorityDumper helper class to parse proto
arguments and set asProto flags.

Bug: 67716082

Test: mmm -j56 frameworks/native/cmds/dumpsys && \
      mmm -j56 frameworks/native/services/utils && \
      adb sync data && \
      adb shell /data/nativetest/dumpsys_test/dumpsys_test && \
      adb shell /data/nativetest64/dumpsys_test/dumpsys_test && \
      adb shell /data/nativetest/prioritydumper_test/prioritydumper_test && \
      adb shell /data/nativetest64/prioritydumper_test/prioritydumper_test && \
      printf "\n\n#### ALL TESTS PASSED ####\n"

Change-Id: I42c2a6a8876efbf9a7d792d68572499b16985147
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 239a2d5..c36ab08 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -62,6 +62,8 @@
             "         --help: shows this help\n"
             "         -l: only list services, do not dump them\n"
             "         -t TIMEOUT: TIMEOUT to use in seconds instead of default 10 seconds\n"
+            "         --proto: filter services that support dumping data in proto format. Dumps"
+            "               will be in proto format.\n"
             "         --priority LEVEL: filter services based on specified priority\n"
             "               LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
             "         --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
@@ -77,17 +79,17 @@
     return false;
 }
 
-static bool ConvertPriorityTypeToBitmask(String16& type, int& bitmask) {
-    if (type == PRIORITY_ARG_CRITICAL) {
-        bitmask = IServiceManager::DUMP_PRIORITY_CRITICAL;
+static bool ConvertPriorityTypeToBitmask(const String16& type, int& bitmask) {
+    if (type == PriorityDumper::PRIORITY_ARG_CRITICAL) {
+        bitmask = IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL;
         return true;
     }
-    if (type == PRIORITY_ARG_HIGH) {
-        bitmask = IServiceManager::DUMP_PRIORITY_HIGH;
+    if (type == PriorityDumper::PRIORITY_ARG_HIGH) {
+        bitmask = IServiceManager::DUMP_FLAG_PRIORITY_HIGH;
         return true;
     }
-    if (type == PRIORITY_ARG_NORMAL) {
-        bitmask = IServiceManager::DUMP_PRIORITY_NORMAL;
+    if (type == PriorityDumper::PRIORITY_ARG_NORMAL) {
+        bitmask = IServiceManager::DUMP_FLAG_PRIORITY_NORMAL;
         return true;
     }
     return false;
@@ -98,11 +100,14 @@
     Vector<String16> args;
     String16 priorityType;
     Vector<String16> skippedServices;
+    Vector<String16> protoServices;
     bool showListOnly = false;
     bool skipServices = false;
+    bool filterByProto = false;
     int timeoutArg = 10;
-    int dumpPriority = IServiceManager::DUMP_PRIORITY_ALL;
+    int dumpPriorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
     static struct option longOptions[] = {{"priority", required_argument, 0, 0},
+                                          {"proto", no_argument, 0, 0},
                                           {"skip", no_argument, 0, 0},
                                           {"help", no_argument, 0, 0},
                                           {0, 0, 0, 0}};
@@ -124,12 +129,14 @@
         case 0:
             if (!strcmp(longOptions[optionIndex].name, "skip")) {
                 skipServices = true;
+            } else if (!strcmp(longOptions[optionIndex].name, "proto")) {
+                filterByProto = true;
             } else if (!strcmp(longOptions[optionIndex].name, "help")) {
                 usage();
                 return 0;
             } else if (!strcmp(longOptions[optionIndex].name, "priority")) {
                 priorityType = String16(String8(optarg));
-                if (!ConvertPriorityTypeToBitmask(priorityType, dumpPriority)) {
+                if (!ConvertPriorityTypeToBitmask(priorityType, dumpPriorityFlags)) {
                     fprintf(stderr, "\n");
                     usage();
                     return -1;
@@ -179,10 +186,19 @@
 
     if (services.empty() || showListOnly) {
         // gets all services
-        services = sm_->listServices(dumpPriority);
+        services = sm_->listServices(dumpPriorityFlags);
         services.sort(sort_func);
-        if (dumpPriority != IServiceManager::DUMP_PRIORITY_ALL) {
-            args.insertAt(String16(PRIORITY_ARG), 0);
+        if (filterByProto) {
+            protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
+            protoServices.sort(sort_func);
+            Vector<String16> intersection;
+            std::set_intersection(services.begin(), services.end(), protoServices.begin(),
+                                  protoServices.end(), std::back_inserter(intersection));
+            services = std::move(intersection);
+            args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
+        }
+        if (dumpPriorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
+            args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
             args.insertAt(priorityType, 1);
         } else {
             args.add(String16("-a"));
@@ -230,7 +246,7 @@
             if (N > 1) {
                 aout << "------------------------------------------------------------"
                         "-------------------" << endl;
-                if (dumpPriority == IServiceManager::DUMP_PRIORITY_ALL) {
+                if (dumpPriorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
                     aout << "DUMP OF SERVICE " << service_name << ":" << endl;
                 } else {
                     aout << "DUMP OF SERVICE " << priorityType << " " << service_name << ":" << endl;
diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp
index 39fcb80..e182b9d 100644
--- a/cmds/dumpsys/tests/Android.bp
+++ b/cmds/dumpsys/tests/Android.bp
@@ -15,6 +15,7 @@
     static_libs: [
         "libdumpsys",
         "libgmock",
+        "libserviceutils",
     ],
 
     clang: true,
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 9fe4572..18a4da9 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -22,6 +22,7 @@
 #include <gtest/gtest.h>
 
 #include <android-base/file.h>
+#include <serviceutils/PriorityDumper.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
@@ -131,16 +132,16 @@
         for (auto& service : services) {
             services16.add(String16(service.c_str()));
         }
-        EXPECT_CALL(sm_, listServices(IServiceManager::DUMP_PRIORITY_ALL))
+        EXPECT_CALL(sm_, listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL))
             .WillRepeatedly(Return(services16));
     }
 
-    void ExpectListServicesWithPriority(std::vector<std::string> services, int dumpPriority) {
+    void ExpectListServicesWithPriority(std::vector<std::string> services, int dumpFlags) {
         Vector<String16> services16;
         for (auto& service : services) {
             services16.add(String16(service.c_str()));
         }
-        EXPECT_CALL(sm_, listServices(dumpPriority)).WillRepeatedly(Return(services16));
+        EXPECT_CALL(sm_, listServices(dumpFlags)).WillRepeatedly(Return(services16));
     }
 
     sp<BinderMock> ExpectCheckService(const char* name, bool running = true) {
@@ -210,6 +211,13 @@
         EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
     }
 
+    void AssertDumpedWithPriority(const std::string& service, const std::string& dump,
+                                  const char16_t* priorityType) {
+        std::string priority = String8(priorityType).c_str();
+        EXPECT_THAT(stdout_,
+                    HasSubstr("DUMP OF SERVICE " + priority + " " + service + ":\n" + dump));
+    }
+
     void AssertNotDumped(const std::string& dump) {
         EXPECT_THAT(stdout_, Not(HasSubstr(dump)));
     }
@@ -250,7 +258,7 @@
 
 // Tests 'dumpsys -l --priority HIGH'
 TEST_F(DumpsysTest, ListAllServicesWithPriority) {
-    ExpectListServicesWithPriority({"Locksmith", "Valet"}, IServiceManager::DUMP_PRIORITY_HIGH);
+    ExpectListServicesWithPriority({"Locksmith", "Valet"}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH);
     ExpectCheckService("Locksmith");
     ExpectCheckService("Valet");
 
@@ -261,13 +269,26 @@
 
 // Tests 'dumpsys -l --priority HIGH' with and empty list
 TEST_F(DumpsysTest, ListEmptyServicesWithPriority) {
-    ExpectListServicesWithPriority({}, IServiceManager::DUMP_PRIORITY_HIGH);
+    ExpectListServicesWithPriority({}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH);
 
     CallMain({"-l", "--priority", "HIGH"});
 
     AssertRunningServices({});
 }
 
+// Tests 'dumpsys -l --proto'
+TEST_F(DumpsysTest, ListAllServicesWithProto) {
+    ExpectListServicesWithPriority({"Locksmith", "Valet", "Car"},
+                                   IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+    ExpectListServicesWithPriority({"Valet", "Car"}, IServiceManager::DUMP_FLAG_PROTO);
+    ExpectCheckService("Car");
+    ExpectCheckService("Valet");
+
+    CallMain({"-l", "--proto"});
+
+    AssertRunningServices({"Car", "Valet"});
+}
+
 // Tests 'dumpsys service_name' on a service is running
 TEST_F(DumpsysTest, DumpRunningService) {
     ExpectDump("Valet", "Here's your car");
@@ -336,7 +357,7 @@
 // Tests 'dumpsys --skip skipped3 skipped5 --priority CRITICAL', which should skip these services
 TEST_F(DumpsysTest, DumpWithSkipAndPriority) {
     ExpectListServicesWithPriority({"running1", "stopped2", "skipped3", "running4", "skipped5"},
-                                   IServiceManager::DUMP_PRIORITY_CRITICAL);
+                                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
     ExpectDump("running1", "dump1");
     ExpectCheckService("stopped2", false);
     ExpectDump("skipped3", "dump3");
@@ -346,8 +367,8 @@
     CallMain({"--priority", "CRITICAL", "--skip", "skipped3", "skipped5"});
 
     AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"});
-    AssertDumped("running1", "dump1");
-    AssertDumped("running4", "dump4");
+    AssertDumpedWithPriority("running1", "dump1", PriorityDumper::PRIORITY_ARG_CRITICAL);
+    AssertDumpedWithPriority("running4", "dump4", PriorityDumper::PRIORITY_ARG_CRITICAL);
     AssertStopped("stopped2");
     AssertNotDumped("dump3");
     AssertNotDumped("dump5");
@@ -356,41 +377,74 @@
 // Tests 'dumpsys --priority CRITICAL'
 TEST_F(DumpsysTest, DumpWithPriorityCritical) {
     ExpectListServicesWithPriority({"runningcritical1", "runningcritical2"},
-                                   IServiceManager::DUMP_PRIORITY_CRITICAL);
+                                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
     ExpectDump("runningcritical1", "dump1");
     ExpectDump("runningcritical2", "dump2");
 
     CallMain({"--priority", "CRITICAL"});
 
     AssertRunningServices({"runningcritical1", "runningcritical2"});
-    AssertDumped("runningcritical1", "dump1");
-    AssertDumped("runningcritical2", "dump2");
+    AssertDumpedWithPriority("runningcritical1", "dump1", PriorityDumper::PRIORITY_ARG_CRITICAL);
+    AssertDumpedWithPriority("runningcritical2", "dump2", PriorityDumper::PRIORITY_ARG_CRITICAL);
 }
 
 // Tests 'dumpsys --priority HIGH'
 TEST_F(DumpsysTest, DumpWithPriorityHigh) {
     ExpectListServicesWithPriority({"runninghigh1", "runninghigh2"},
-                                   IServiceManager::DUMP_PRIORITY_HIGH);
+                                   IServiceManager::DUMP_FLAG_PRIORITY_HIGH);
     ExpectDump("runninghigh1", "dump1");
     ExpectDump("runninghigh2", "dump2");
 
     CallMain({"--priority", "HIGH"});
 
     AssertRunningServices({"runninghigh1", "runninghigh2"});
-    AssertDumped("runninghigh1", "dump1");
-    AssertDumped("runninghigh2", "dump2");
+    AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH);
+    AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH);
 }
 
 // Tests 'dumpsys --priority NORMAL'
 TEST_F(DumpsysTest, DumpWithPriorityNormal) {
     ExpectListServicesWithPriority({"runningnormal1", "runningnormal2"},
-                                   IServiceManager::DUMP_PRIORITY_NORMAL);
+                                   IServiceManager::DUMP_FLAG_PRIORITY_NORMAL);
     ExpectDump("runningnormal1", "dump1");
     ExpectDump("runningnormal2", "dump2");
 
     CallMain({"--priority", "NORMAL"});
 
     AssertRunningServices({"runningnormal1", "runningnormal2"});
-    AssertDumped("runningnormal1", "dump1");
-    AssertDumped("runningnormal2", "dump2");
+    AssertDumpedWithPriority("runningnormal1", "dump1", PriorityDumper::PRIORITY_ARG_NORMAL);
+    AssertDumpedWithPriority("runningnormal2", "dump2", PriorityDumper::PRIORITY_ARG_NORMAL);
+}
+
+// Tests 'dumpsys --proto'
+TEST_F(DumpsysTest, DumpWithProto) {
+    ExpectListServicesWithPriority({"run8", "run1", "run2", "run5"},
+                                   IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+    ExpectListServicesWithPriority({"run3", "run2", "run4", "run8"},
+                                   IServiceManager::DUMP_FLAG_PROTO);
+    ExpectDump("run2", "dump1");
+    ExpectDump("run8", "dump2");
+
+    CallMain({"--proto"});
+
+    AssertRunningServices({"run2", "run8"});
+    AssertDumped("run2", "dump1");
+    AssertDumped("run8", "dump2");
+}
+
+// Tests 'dumpsys --priority HIGH --proto'
+TEST_F(DumpsysTest, DumpWithPriorityHighAndProto) {
+    ExpectListServicesWithPriority({"runninghigh1", "runninghigh2"},
+                                   IServiceManager::DUMP_FLAG_PRIORITY_HIGH);
+    ExpectListServicesWithPriority({"runninghigh1", "runninghigh2", "runninghigh3"},
+                                   IServiceManager::DUMP_FLAG_PROTO);
+
+    ExpectDump("runninghigh1", "dump1");
+    ExpectDump("runninghigh2", "dump2");
+
+    CallMain({"--priority", "HIGH", "--proto"});
+
+    AssertRunningServices({"runninghigh1", "runninghigh2"});
+    AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH);
+    AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH);
 }
diff --git a/libs/binder/include/binder/BinderService.h b/libs/binder/include/binder/BinderService.h
index 4e69067..4ce82a1 100644
--- a/libs/binder/include/binder/BinderService.h
+++ b/libs/binder/include/binder/BinderService.h
@@ -35,15 +35,16 @@
 {
 public:
     static status_t publish(bool allowIsolated = false,
-                            int dumpPriority = IServiceManager::DUMP_PRIORITY_NORMAL) {
+                            int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
         sp<IServiceManager> sm(defaultServiceManager());
         return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,
-                              dumpPriority);
+                              dumpFlags);
     }
 
-    static void publishAndJoinThreadPool(bool allowIsolated = false,
-                                         int dumpPriority = IServiceManager::DUMP_PRIORITY_NORMAL) {
-        publish(allowIsolated, dumpPriority);
+    static void publishAndJoinThreadPool(
+            bool allowIsolated = false,
+            int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+        publish(allowIsolated, dumpFlags);
         joinThreadPool();
     }
 
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 78b03bd..19e841a 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -34,11 +34,12 @@
     /*
      * Must match values in IServiceManager.java
      */
-    static const int DUMP_PRIORITY_CRITICAL = 1 << 0;
-    static const int DUMP_PRIORITY_HIGH = 1 << 1;
-    static const int DUMP_PRIORITY_NORMAL = 1 << 2;
-    static const int DUMP_PRIORITY_ALL =
-            DUMP_PRIORITY_CRITICAL | DUMP_PRIORITY_HIGH | DUMP_PRIORITY_NORMAL;
+    static const int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0;
+    static const int DUMP_FLAG_PRIORITY_HIGH = 1 << 1;
+    static const int DUMP_FLAG_PRIORITY_NORMAL = 1 << 2;
+    static const int DUMP_FLAG_PRIORITY_ALL =
+            DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL;
+    static const int DUMP_FLAG_PROTO = 1 << 3;
 
     /**
      * Retrieve an existing service, blocking for a few seconds
@@ -56,12 +57,12 @@
      */
     virtual status_t addService(const String16& name, const sp<IBinder>& service,
                                 bool allowIsolated = false,
-                                int dumpsysPriority = DUMP_PRIORITY_NORMAL) = 0;
+                                int dumpsysFlags = DUMP_FLAG_PRIORITY_NORMAL) = 0;
 
     /**
      * Return list of all existing services.
      */
-    virtual Vector<String16> listServices(int dumpsysPriority = DUMP_PRIORITY_ALL) = 0;
+    virtual Vector<String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0;
 
     enum {
         GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5982422..63ccba7 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3459,12 +3459,18 @@
 
 // ---------------------------------------------------------------------------
 
-status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args) {
+status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asProto) {
     String8 result;
 
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
     const int uid = ipc->getCallingUid();
+
+    if (asProto) {
+        // Return early as SurfaceFlinger does not support dumping sections in proto format
+        return OK;
+    }
+
     if ((uid != AID_SHELL) &&
             !PermissionCache::checkPermission(sDump, pid, uid)) {
         result.appendFormat("Permission Denial: "
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 2cba500..0fc6a7e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -596,11 +596,14 @@
      * Debugging & dumpsys
      */
 public:
-    status_t dumpCritical(int fd, const Vector<String16>& /*args*/) {
-        return doDump(fd, Vector<String16>());
+    status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) {
+        return doDump(fd, Vector<String16>(), asProto);
     }
 
-    status_t dumpAll(int fd, const Vector<String16>& args) { return doDump(fd, args); }
+    status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) {
+        return doDump(fd, args, asProto);
+    }
+
 private:
     void listLayersLocked(const Vector<String16>& args, size_t& index, String8& result) const;
     void dumpStatsLocked(const Vector<String16>& args, size_t& index, String8& result) const;
@@ -626,7 +629,7 @@
     bool isLayerTripleBufferingDisabled() const {
         return this->mLayerTripleBufferingDisabled;
     }
-    status_t doDump(int fd, const Vector<String16>& args);
+    status_t doDump(int fd, const Vector<String16>& args, bool asProto);
 
 #ifdef USE_HWC2
     /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index b718ec8..81bda02 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -3005,13 +3005,18 @@
 
 // ---------------------------------------------------------------------------
 
-status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args)
-{
+status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asProto) {
     String8 result;
 
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
     const int uid = ipc->getCallingUid();
+
+    if (asProto) {
+        // Return early as SurfaceFlinger does not support dumping sections in proto format
+        return OK;
+    }
+
     if ((uid != AID_SHELL) &&
             !PermissionCache::checkPermission(sDump, pid, uid)) {
         result.appendFormat("Permission Denial: "
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 6a24891..2a924ae 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -106,7 +106,7 @@
     // publish surface flinger
     sp<IServiceManager> sm(defaultServiceManager());
     sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
-                   IServiceManager::DUMP_PRIORITY_CRITICAL);
+                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
 
     // publish GpuService
     sp<GpuService> gpuservice = new GpuService();
diff --git a/services/utils/PriorityDumper.cpp b/services/utils/PriorityDumper.cpp
index 9851188..967dee5 100644
--- a/services/utils/PriorityDumper.cpp
+++ b/services/utils/PriorityDumper.cpp
@@ -18,41 +18,68 @@
 
 namespace android {
 
-static void getStrippedArgs(Vector<String16>& dest, const Vector<String16>& source,
-                            std::size_t numArgsToStrip) {
-    for (auto it = source.begin() + numArgsToStrip; it != source.end(); it++) {
-        dest.add(*it);
+const char16_t PriorityDumper::PROTO_ARG[] = u"--proto";
+const char16_t PriorityDumper::PRIORITY_ARG[] = u"--dump-priority";
+const char16_t PriorityDumper::PRIORITY_ARG_CRITICAL[] = u"CRITICAL";
+const char16_t PriorityDumper::PRIORITY_ARG_HIGH[] = u"HIGH";
+const char16_t PriorityDumper::PRIORITY_ARG_NORMAL[] = u"NORMAL";
+
+enum class PriorityType { INVALID, CRITICAL, HIGH, NORMAL };
+
+static PriorityType getPriorityType(const String16& arg) {
+    if (arg == PriorityDumper::PRIORITY_ARG_CRITICAL) {
+        return PriorityType::CRITICAL;
+    } else if (arg == PriorityDumper::PRIORITY_ARG_HIGH) {
+        return PriorityType::HIGH;
+    } else if (arg == PriorityDumper::PRIORITY_ARG_NORMAL) {
+        return PriorityType::NORMAL;
     }
+    return PriorityType::INVALID;
 }
 
-status_t PriorityDumper::dumpAll(int fd, const Vector<String16>& args) {
+status_t PriorityDumper::dumpAll(int fd, const Vector<String16>& args, bool asProto) {
     status_t status;
-    status = dumpCritical(fd, args);
+    status = dumpCritical(fd, args, asProto);
     if (status != OK) return status;
-    status = dumpHigh(fd, args);
+    status = dumpHigh(fd, args, asProto);
     if (status != OK) return status;
-    status = dumpNormal(fd, args);
+    status = dumpNormal(fd, args, asProto);
     if (status != OK) return status;
     return status;
 }
 
 status_t PriorityDumper::priorityDump(int fd, const Vector<String16>& args) {
     status_t status;
-    if (args.size() >= 2 && args[0] == PRIORITY_ARG) {
-        String16 priority = args[1];
-        Vector<String16> strippedArgs;
-        getStrippedArgs(strippedArgs, args, 2);
-        if (priority == PRIORITY_ARG_CRITICAL) {
-            status = dumpCritical(fd, strippedArgs);
-        } else if (priority == PRIORITY_ARG_HIGH) {
-            status = dumpHigh(fd, strippedArgs);
-        } else if (priority == PRIORITY_ARG_NORMAL) {
-            status = dumpNormal(fd, strippedArgs);
+    bool asProto = false;
+    PriorityType priority = PriorityType::INVALID;
+
+    Vector<String16> strippedArgs;
+    for (uint32_t argIndex = 0; argIndex < args.size(); argIndex++) {
+        if (args[argIndex] == PROTO_ARG) {
+            asProto = true;
+        } else if (args[argIndex] == PRIORITY_ARG) {
+            if (argIndex + 1 < args.size()) {
+                argIndex++;
+                priority = getPriorityType(args[argIndex]);
+            }
         } else {
-            status = dumpAll(fd, args);
+            strippedArgs.add(args[argIndex]);
         }
-    } else {
-        status = dumpAll(fd, args);
+    }
+
+    switch (priority) {
+        case PriorityType::CRITICAL:
+            status = dumpCritical(fd, strippedArgs, asProto);
+            break;
+        case PriorityType::HIGH:
+            status = dumpHigh(fd, strippedArgs, asProto);
+            break;
+        case PriorityType::NORMAL:
+            status = dumpNormal(fd, strippedArgs, asProto);
+            break;
+        default:
+            status = dumpAll(fd, strippedArgs, asProto);
+            break;
     }
     return status;
 }
diff --git a/services/utils/include/serviceutils/PriorityDumper.h b/services/utils/include/serviceutils/PriorityDumper.h
index 0319242..d01a102 100644
--- a/services/utils/include/serviceutils/PriorityDumper.h
+++ b/services/utils/include/serviceutils/PriorityDumper.h
@@ -23,34 +23,43 @@
 
 namespace android {
 
-constexpr const char16_t PRIORITY_ARG[] = u"--dump-priority";
-constexpr const char16_t PRIORITY_ARG_CRITICAL[] = u"CRITICAL";
-constexpr const char16_t PRIORITY_ARG_HIGH[] = u"HIGH";
-constexpr const char16_t PRIORITY_ARG_NORMAL[] = u"NORMAL";
-
-// Helper class to split dumps into various priority buckets.
+// Helper class to parse common arguments responsible for splitting dumps into
+// various priority buckets and changing the output format of the dump.
 class PriorityDumper {
 public:
-    // Parses the argument list checking if the first argument is --dump_priority and
-    // the second argument is the priority type (HIGH, CRITICAL or NORMAL). If the
-    // arguments are found, they are stripped and the appropriate PriorityDumper
-    // method is called.
-    // If --dump_priority argument is not passed, all supported sections are dumped.
+    static const char16_t PRIORITY_ARG[];
+    static const char16_t PRIORITY_ARG_CRITICAL[];
+    static const char16_t PRIORITY_ARG_HIGH[];
+    static const char16_t PRIORITY_ARG_NORMAL[];
+    static const char16_t PROTO_ARG[];
+
+    // Parses the argument list searching for --dump_priority with a priority type
+    // (HIGH, CRITICAL or NORMAL) and --proto. Matching arguments are stripped.
+    // If a valid priority type is found, the associated PriorityDumper
+    // method is called otherwise all supported sections are dumped.
+    // If --proto is found, the dumpAsProto flag is set to dump sections in proto
+    // format.
     status_t priorityDump(int fd, const Vector<String16>& args);
 
     // Dumps CRITICAL priority sections.
-    virtual status_t dumpCritical(int /*fd*/, const Vector<String16>& /*args*/) { return OK; }
+    virtual status_t dumpCritical(int /*fd*/, const Vector<String16>& /*args*/, bool /*asProto*/) {
+        return OK;
+    }
 
     // Dumps HIGH priority sections.
-    virtual status_t dumpHigh(int /*fd*/, const Vector<String16>& /*args*/) { return OK; }
+    virtual status_t dumpHigh(int /*fd*/, const Vector<String16>& /*args*/, bool /*asProto*/) {
+        return OK;
+    }
 
     // Dumps normal priority sections.
-    virtual status_t dumpNormal(int /*fd*/, const Vector<String16>& /*args*/) { return OK; }
+    virtual status_t dumpNormal(int /*fd*/, const Vector<String16>& /*args*/, bool /*asProto*/) {
+        return OK;
+    }
 
     // Dumps all sections.
     // This method is called when priorityDump is called without priority
     // arguments. By default, it calls all three dump methods.
-    virtual status_t dumpAll(int fd, const Vector<String16>& args);
+    virtual status_t dumpAll(int fd, const Vector<String16>& args, bool asProto);
     virtual ~PriorityDumper() = default;
 };
 
diff --git a/services/utils/tests/PriorityDumper_test.cpp b/services/utils/tests/PriorityDumper_test.cpp
index 79e7340..90cc6de 100644
--- a/services/utils/tests/PriorityDumper_test.cpp
+++ b/services/utils/tests/PriorityDumper_test.cpp
@@ -32,17 +32,17 @@
 
 class PriorityDumperMock : public PriorityDumper {
 public:
-    MOCK_METHOD2(dumpCritical, status_t(int, const Vector<String16>&));
-    MOCK_METHOD2(dumpHigh, status_t(int, const Vector<String16>&));
-    MOCK_METHOD2(dumpNormal, status_t(int, const Vector<String16>&));
-    MOCK_METHOD2(dumpAll, status_t(int, const Vector<String16>&));
+    MOCK_METHOD3(dumpCritical, status_t(int, const Vector<String16>&, bool));
+    MOCK_METHOD3(dumpHigh, status_t(int, const Vector<String16>&, bool));
+    MOCK_METHOD3(dumpNormal, status_t(int, const Vector<String16>&, bool));
+    MOCK_METHOD3(dumpAll, status_t(int, const Vector<String16>&, bool));
 };
 
 class DumpAllMock : public PriorityDumper {
 public:
-    MOCK_METHOD2(dumpCritical, status_t(int, const Vector<String16>&));
-    MOCK_METHOD2(dumpHigh, status_t(int, const Vector<String16>&));
-    MOCK_METHOD2(dumpNormal, status_t(int, const Vector<String16>&));
+    MOCK_METHOD3(dumpCritical, status_t(int, const Vector<String16>&, bool));
+    MOCK_METHOD3(dumpHigh, status_t(int, const Vector<String16>&, bool));
+    MOCK_METHOD3(dumpNormal, status_t(int, const Vector<String16>&, bool));
 };
 
 class PriorityDumperTest : public Test {
@@ -61,14 +61,14 @@
 
 TEST_F(PriorityDumperTest, noArgsPassed) {
     Vector<String16> args;
-    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(args)));
+    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(args), /*asProto=*/false));
     dumper_.priorityDump(fd, args);
 }
 
 TEST_F(PriorityDumperTest, noPriorityArgsPassed) {
     Vector<String16> args;
     addAll(args, {"bunch", "of", "args"});
-    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(args)));
+    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(args), /*asProto=*/false));
     dumper_.priorityDump(fd, args);
 }
 
@@ -76,7 +76,7 @@
     Vector<String16> args;
     addAll(args, {"--dump-priority", "CRITICAL"});
     Vector<String16> strippedArgs;
-    EXPECT_CALL(dumper_, dumpCritical(fd, ElementsAreArray(strippedArgs)));
+    EXPECT_CALL(dumper_, dumpCritical(fd, ElementsAreArray(strippedArgs), /*asProto=*/false));
     dumper_.priorityDump(fd, args);
 }
 
@@ -86,7 +86,17 @@
     Vector<String16> strippedArgs;
     addAll(strippedArgs, {"args", "left", "behind"});
 
-    EXPECT_CALL(dumper_, dumpCritical(fd, ElementsAreArray(strippedArgs)));
+    EXPECT_CALL(dumper_, dumpCritical(fd, ElementsAreArray(strippedArgs), /*asProto=*/false));
+    dumper_.priorityDump(fd, args);
+}
+
+TEST_F(PriorityDumperTest, dumpCriticalInMiddle) {
+    Vector<String16> args;
+    addAll(args, {"args", "left", "--dump-priority", "CRITICAL", "behind"});
+    Vector<String16> strippedArgs;
+    addAll(strippedArgs, {"args", "left", "behind"});
+
+    EXPECT_CALL(dumper_, dumpCritical(fd, ElementsAreArray(strippedArgs), /*asProto=*/false));
     dumper_.priorityDump(fd, args);
 }
 
@@ -96,7 +106,17 @@
     Vector<String16> strippedArgs;
     addAll(strippedArgs, {"args", "left", "behind"});
 
-    EXPECT_CALL(dumper_, dumpHigh(fd, ElementsAreArray(strippedArgs)));
+    EXPECT_CALL(dumper_, dumpHigh(fd, ElementsAreArray(strippedArgs), /*asProto=*/false));
+    dumper_.priorityDump(fd, args);
+}
+
+TEST_F(PriorityDumperTest, dumpHighInEnd) {
+    Vector<String16> args;
+    addAll(args, {"args", "left", "behind", "--dump-priority", "HIGH"});
+    Vector<String16> strippedArgs;
+    addAll(strippedArgs, {"args", "left", "behind"});
+
+    EXPECT_CALL(dumper_, dumpHigh(fd, ElementsAreArray(strippedArgs), /*asProto=*/false));
     dumper_.priorityDump(fd, args);
 }
 
@@ -106,7 +126,7 @@
     Vector<String16> strippedArgs;
     addAll(strippedArgs, {"args", "left", "behind"});
 
-    EXPECT_CALL(dumper_, dumpNormal(fd, ElementsAreArray(strippedArgs)));
+    EXPECT_CALL(dumper_, dumpNormal(fd, ElementsAreArray(strippedArgs), /*asProto=*/false));
     dumper_.priorityDump(fd, args);
 }
 
@@ -114,9 +134,9 @@
     Vector<String16> args;
     addAll(args, {"args", "left", "behind"});
 
-    EXPECT_CALL(dumpAlldumper_, dumpCritical(fd, ElementsAreArray(args)));
-    EXPECT_CALL(dumpAlldumper_, dumpHigh(fd, ElementsAreArray(args)));
-    EXPECT_CALL(dumpAlldumper_, dumpNormal(fd, ElementsAreArray(args)));
+    EXPECT_CALL(dumpAlldumper_, dumpCritical(fd, ElementsAreArray(args), /*asProto=*/false));
+    EXPECT_CALL(dumpAlldumper_, dumpHigh(fd, ElementsAreArray(args), /*asProto=*/false));
+    EXPECT_CALL(dumpAlldumper_, dumpNormal(fd, ElementsAreArray(args), /*asProto=*/false));
 
     dumpAlldumper_.priorityDump(fd, args);
 }
@@ -124,7 +144,8 @@
 TEST_F(PriorityDumperTest, priorityArgWithPriorityMissing) {
     Vector<String16> args;
     addAll(args, {"--dump-priority"});
-    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(args)));
+    Vector<String16> strippedArgs;
+    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(strippedArgs), /*asProto=*/false));
 
     dumper_.priorityDump(fd, args);
 }
@@ -132,7 +153,67 @@
 TEST_F(PriorityDumperTest, priorityArgWithInvalidPriority) {
     Vector<String16> args;
     addAll(args, {"--dump-priority", "REALLY_HIGH"});
-    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(args)));
+    Vector<String16> strippedArgs;
+    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(strippedArgs), /*asProto=*/false));
+
+    dumper_.priorityDump(fd, args);
+}
+
+TEST_F(PriorityDumperTest, protoArg) {
+    Vector<String16> args;
+    addAll(args, {"--proto"});
+    Vector<String16> strippedArgs;
+    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(strippedArgs), /*asProto=*/true));
+
+    dumper_.priorityDump(fd, args);
+}
+
+TEST_F(PriorityDumperTest, protoArgWithPriorityArgs) {
+    Vector<String16> args;
+    addAll(args, {"--proto", "args", "--dump-priority", "NORMAL", "left", "behind"});
+    Vector<String16> strippedArgs;
+    addAll(strippedArgs, {"args", "left", "behind"});
+    EXPECT_CALL(dumper_, dumpNormal(fd, ElementsAreArray(strippedArgs), /*asProto=*/true));
+
+    dumper_.priorityDump(fd, args);
+}
+
+TEST_F(PriorityDumperTest, protoArgWithPriorityArgsInReverseOrder) {
+    Vector<String16> args;
+    addAll(args, {"--dump-priority", "NORMAL", "--proto", "args", "left", "behind"});
+    Vector<String16> strippedArgs;
+    addAll(strippedArgs, {"args", "left", "behind"});
+    EXPECT_CALL(dumper_, dumpNormal(fd, ElementsAreArray(strippedArgs), /*asProto=*/true));
+
+    dumper_.priorityDump(fd, args);
+}
+
+TEST_F(PriorityDumperTest, protoArgInMiddle) {
+    Vector<String16> args;
+    addAll(args, {"--unknown", "args", "--proto", "args", "left", "behind"});
+    Vector<String16> strippedArgs;
+    addAll(strippedArgs, {"--unknown", "args", "args", "left", "behind"});
+    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(strippedArgs), /*asProto=*/true));
+
+    dumper_.priorityDump(fd, args);
+}
+
+TEST_F(PriorityDumperTest, protoArgAtEnd) {
+    Vector<String16> args;
+    addAll(args, {"--unknown", "args", "args", "left", "behind", "--proto"});
+    Vector<String16> strippedArgs;
+    addAll(strippedArgs, {"--unknown", "args", "args", "left", "behind"});
+    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(strippedArgs), /*asProto=*/true));
+
+    dumper_.priorityDump(fd, args);
+}
+
+TEST_F(PriorityDumperTest, protoArgWithInvalidPriorityType) {
+    Vector<String16> args;
+    addAll(args, {"--dump-priority", "NOT_SO_HIGH", "--proto", "args", "left", "behind"});
+    Vector<String16> strippedArgs;
+    addAll(strippedArgs, {"args", "left", "behind"});
+    EXPECT_CALL(dumper_, dumpAll(fd, ElementsAreArray(strippedArgs), /*asProto=*/true));
 
     dumper_.priorityDump(fd, args);
 }
\ No newline at end of file