Differentiate system and test apis in whitelist

@SystemApi and @TestApi entries in the whitelist can now be
differentiated from the rest of the public apis.

Test: m test-art-host-gtest-hiddenapi_test
Change-Id: I2929cd5d48b760af92fc3cc7061039da9fe94f67
diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h
index c468723..e6b1879 100644
--- a/libartbase/base/hiddenapi_flags.h
+++ b/libartbase/base/hiddenapi_flags.h
@@ -25,6 +25,7 @@
 #include "base/bit_utils.h"
 #include "base/dumpable.h"
 #include "base/macros.h"
+#include "base/hiddenapi_stubs.h"
 
 namespace art {
 namespace hiddenapi {
@@ -209,6 +210,12 @@
     for (std::vector<std::string>::iterator it = begin; it != end; it++) {
       ApiList current = FromName(*it);
       if (current.IsEmpty() || !api_list.CanCombineWith(current)) {
+        if (ApiStubs::IsStubsFlag(*it)) {
+        // Ignore flags which correspond to the stubs from where the api
+        // originates (i.e. system-api, test-api, public-api), as they are not
+        // relevant at runtime
+          continue;
+        }
         return false;
       }
       api_list |= current;
diff --git a/libartbase/base/hiddenapi_stubs.h b/libartbase/base/hiddenapi_stubs.h
new file mode 100644
index 0000000..94ef95c
--- /dev/null
+++ b/libartbase/base/hiddenapi_stubs.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_HIDDENAPI_STUBS_H_
+#define ART_LIBARTBASE_BASE_HIDDENAPI_STUBS_H_
+
+#include <set>
+#include <string_view>
+
+namespace art {
+namespace hiddenapi {
+
+class ApiStubs {
+ public:
+  enum class Kind {
+    kPublicApi,
+    kSystemApi,
+    kTestApi,
+    kCorePlatformApi,
+  };
+
+  static const std::string_view ToString(Kind api) {
+    switch (api) {
+      case Kind::kPublicApi:
+        return kPublicApiStr;
+      case Kind::kSystemApi:
+        return kSystemApiStr;
+      case Kind::kTestApi:
+        return kTestApiStr;
+      case Kind::kCorePlatformApi:
+        return kCorePlatformApiStr;
+    }
+  }
+
+  static bool IsStubsFlag(const std::string_view& api_flag_name) {
+    return api_flag_name == kPublicApiStr || api_flag_name == kSystemApiStr ||
+        api_flag_name == kTestApiStr || api_flag_name == kCorePlatformApiStr;
+  }
+
+ private:
+  static constexpr std::string_view kPublicApiStr{"public-api"};
+  static constexpr std::string_view kSystemApiStr{"system-api"};
+  static constexpr std::string_view kTestApiStr{"test-api"};
+  static constexpr std::string_view kCorePlatformApiStr{"core-platform-api"};
+};
+
+}  // namespace hiddenapi
+}  // namespace art
+
+
+#endif  // ART_LIBARTBASE_BASE_HIDDENAPI_STUBS_H_
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index 9c4e57e..01b36a4 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -60,6 +60,8 @@
     public static final String FLAG_GREYLIST_MAX_O = "greylist-max-o";
     public static final String FLAG_GREYLIST_MAX_P = "greylist-max-p";
 
+    public static final String FLAG_PUBLIC_API = "public-api";
+
     private static final Map<Integer, String> TARGET_SDK_TO_LIST_MAP;
     static {
         Map<Integer, String> map = new HashMap<>();
@@ -178,7 +180,7 @@
             mPublicApis =
                     Files.readLines(new File(stubApiFlagsFile), Charset.forName("UTF-8")).stream()
                         .map(s -> Splitter.on(",").splitToList(s))
-                        .filter(s -> s.contains(FLAG_WHITELIST))
+                        .filter(s -> s.contains(FLAG_PUBLIC_API))
                         .map(s -> s.get(0))
                         .collect(Collectors.toSet());
         } else {
@@ -199,7 +201,7 @@
                 greylistAnnotationHandler));
 
         CovariantReturnTypeHandler covariantReturnTypeHandler = new CovariantReturnTypeHandler(
-            mOutput, mPublicApis, FLAG_WHITELIST);
+            mOutput, mPublicApis, FLAG_PUBLIC_API);
 
         return addRepeatedAnnotationHandlers(builder, CovariantReturnTypeHandler.ANNOTATION_NAME,
             CovariantReturnTypeHandler.REPEATED_ANNOTATION_NAME, covariantReturnTypeHandler)
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index 3390a99..937abd1 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -88,6 +88,8 @@
   UsageError("  Command \"list\": dump lists of public and private API");
   UsageError("    --boot-dex=<filename>: dex file which belongs to boot class path");
   UsageError("    --public-stub-classpath=<filenames>:");
+  UsageError("    --system-stub-classpath=<filenames>:");
+  UsageError("    --test-stub-classpath=<filenames>:");
   UsageError("    --core-platform-stub-classpath=<filenames>:");
   UsageError("        colon-separated list of dex/apk files which form API stubs of boot");
   UsageError("        classpath. Multiple classpaths can be specified");
@@ -906,11 +908,19 @@
           } else if (StartsWith(option, "--public-stub-classpath=")) {
             stub_classpaths_.push_back(std::make_pair(
                 std::string(option.substr(strlen("--public-stub-classpath="))),
-                ApiList::Whitelist()));
+                ApiStubs::Kind::kPublicApi));
+          } else if (StartsWith(option, "--system-stub-classpath=")) {
+            stub_classpaths_.push_back(std::make_pair(
+                std::string(option.substr(strlen("--system-stub-classpath="))),
+                ApiStubs::Kind::kSystemApi));
+          } else if (StartsWith(option, "--test-stub-classpath=")) {
+            stub_classpaths_.push_back(std::make_pair(
+                std::string(option.substr(strlen("--test-stub-classpath="))),
+                ApiStubs::Kind::kTestApi));
           } else if (StartsWith(option, "--core-platform-stub-classpath=")) {
             stub_classpaths_.push_back(std::make_pair(
                 std::string(option.substr(strlen("--core-platform-stub-classpath="))),
-                ApiList::CorePlatformApi()));
+                ApiStubs::Kind::kCorePlatformApi));
           } else if (StartsWith(option, "--out-api-flags=")) {
             api_flags_path_ = std::string(option.substr(strlen("--out-api-flags=")));
           } else {
@@ -983,15 +993,19 @@
 
     size_t line_number = 1;
     for (std::string line; std::getline(api_file, line); line_number++) {
+      // Every line contains a comma separated list with the signature as the
+      // first element and the api flags as the rest
       std::vector<std::string> values = android::base::Split(line, ",");
       CHECK_GT(values.size(), 1u) << path << ":" << line_number
           << ": No flags found: " << line << kErrorHelp;
 
       const std::string& signature = values[0];
+
       CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number
           << ": Duplicate entry: " << signature << kErrorHelp;
 
       ApiList membership;
+
       bool success = ApiList::FromNames(values.begin() + 1, values.end(), &membership);
       CHECK(success) << path << ":" << line_number
           << ": Some flags were not recognized: " << line << kErrorHelp;
@@ -1016,7 +1030,7 @@
 
     // Complete list of boot class path members. The associated boolean states
     // whether it is public (true) or private (false).
-    std::map<std::string, ApiList> boot_members;
+    std::map<std::string, std::set<std::string_view>> boot_members;
 
     // Deduplicate errors before printing them.
     std::set<std::string> unresolved;
@@ -1027,7 +1041,7 @@
 
     // Mark all boot dex members private.
     boot_classpath.ForEachDexMember([&](const DexMember& boot_member) {
-      boot_members[boot_member.GetApiEntry()] = ApiList();
+      boot_members[boot_member.GetApiEntry()] = {};
     });
 
     // Resolve each SDK dex member against the framework and mark it white.
@@ -1035,7 +1049,7 @@
       ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"),
                                /* open_writable= */ false);
       Hierarchy stub_hierarchy(stub_classpath);
-      const ApiList stub_api_list = cp_entry.second;
+      const ApiStubs::Kind stub_api = cp_entry.second;
 
       stub_classpath.ForEachDexMember(
           [&](const DexMember& stub_member) {
@@ -1049,7 +1063,7 @@
                   std::string entry = boot_member.GetApiEntry();
                   auto it = boot_members.find(entry);
                   CHECK(it != boot_members.end());
-                  it->second |= stub_api_list;
+                  it->second.insert(ApiStubs::ToString(stub_api));
                 });
             if (!resolved) {
               unresolved.insert(stub_member.GetApiEntry());
@@ -1065,10 +1079,11 @@
     // Write into public/private API files.
     std::ofstream file_flags(api_flags_path_.c_str());
     for (const auto& entry : boot_members) {
-      if (entry.second.IsEmpty()) {
+      if (entry.second.empty()) {
         file_flags << entry.first << std::endl;
       } else {
-        file_flags << entry.first << "," << entry.second << std::endl;
+        file_flags << entry.first << ",";
+        file_flags << android::base::Join(entry.second, ",") << std::endl;
       }
     }
     file_flags.close();
@@ -1086,7 +1101,7 @@
 
   // Set of public API stub classpaths. Each classpath is formed by a list
   // of DEX/APK files in the order they appear on the classpath.
-  std::vector<std::pair<std::string, ApiList>> stub_classpaths_;
+  std::vector<std::pair<std::string, ApiStubs::Kind>> stub_classpaths_;
 
   // Path to CSV file containing the list of API members and their flags.
   // This could be both an input and output path.
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 74feb8a..213692b 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -706,7 +706,7 @@
   ScratchFile flags_csv;
   ASSERT_TRUE(RunHiddenapiList(flags_csv));
   auto flags = ReadFlagsCsvFile(flags_csv);
-  ASSERT_EQ(SafeMapGet("LPackageClass;->publicMethod1()V", flags), "whitelist");
+  ASSERT_EQ(SafeMapGet("LPackageClass;->publicMethod1()V", flags), "public-api");
 }
 
 // Test a method declared in PublicInterface, defined in AbstractPackageClass and
@@ -715,7 +715,7 @@
   ScratchFile flags_csv;
   ASSERT_TRUE(RunHiddenapiList(flags_csv));
   auto flags = ReadFlagsCsvFile(flags_csv);
-  ASSERT_EQ(SafeMapGet("LAbstractPackageClass;->publicMethod2()V", flags), "whitelist");
+  ASSERT_EQ(SafeMapGet("LAbstractPackageClass;->publicMethod2()V", flags), "public-api");
 }
 
 }  // namespace art