Allow excluding API list values via command line arguments.

This replaces --only-report-sdk-uses as well. Note only ApiList::Value and not DomainApi are supported for now.

Test: manual
Change-Id: I7734eae1ebb0f922eb4652d29f1e1e4dce18cbec
diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h
index e79e5f3..3f498bf 100644
--- a/libartbase/base/hiddenapi_flags.h
+++ b/libartbase/base/hiddenapi_flags.h
@@ -241,6 +241,7 @@
 
   bool operator==(const ApiList& other) const { return dex_flags_ == other.dex_flags_; }
   bool operator!=(const ApiList& other) const { return !(*this == other); }
+  bool operator<(const ApiList& other) const { return dex_flags_ < other.dex_flags_; }
 
   // Returns true if combining this ApiList with `other` will succeed.
   bool CanCombineWith(const ApiList& other) const {
diff --git a/tools/veridex/api_list_filter.h b/tools/veridex/api_list_filter.h
new file mode 100644
index 0000000..29929f6
--- /dev/null
+++ b/tools/veridex/api_list_filter.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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_TOOLS_VERIDEX_API_LIST_FILTER_H_
+#define ART_TOOLS_VERIDEX_API_LIST_FILTER_H_
+
+#include <algorithm>
+#include <android-base/strings.h>
+
+#include "base/hiddenapi_flags.h"
+
+namespace art {
+
+class ApiListFilter {
+ public:
+  explicit ApiListFilter(const std::vector<std::string>& exclude_api_lists) {
+    std::set<hiddenapi::ApiList> exclude_set;
+    for (const std::string& name : exclude_api_lists) {
+      hiddenapi::ApiList list = hiddenapi::ApiList::FromName(name);
+      if (!list.IsValid()) {
+        LOG(ERROR) << "Unknown ApiList::Value " << name
+                   << ". See valid values in art/libartbase/base/hiddenapi_flags.h.";
+      }
+      exclude_set.insert(list);
+    }
+
+    for (size_t i = 0; i < hiddenapi::ApiList::kValueCount; ++i) {
+      hiddenapi::ApiList list = hiddenapi::ApiList(i);
+      if (exclude_set.find(list) == exclude_set.end()) {
+          lists_.push_back(list);
+      }
+    }
+  }
+
+  bool Matches(hiddenapi::ApiList list) const {
+    for (const auto& it : lists_) {
+      if (list.GetIntValue() == it.GetIntValue()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+ private:
+  std::vector<hiddenapi::ApiList> lists_;
+};
+
+}  // namespace art
+
+#endif  // ART_TOOLS_VERIDEX_API_LIST_FILTER_H_
diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh
index 99537a4..f8e935e 100755
--- a/tools/veridex/appcompat.sh
+++ b/tools/veridex/appcompat.sh
@@ -28,6 +28,7 @@
   exec ${SCRIPT_DIR}/veridex \
     --core-stubs=${SCRIPT_DIR}/system-stubs.zip:${SCRIPT_DIR}/org.apache.http.legacy-stubs.zip \
     --api-flags=${SCRIPT_DIR}/hiddenapi-flags.csv \
+    --exclude-api-lists=whitelist \
     $@
 fi
 
@@ -69,6 +70,10 @@
   extra_flags="--api-flags=$file"
 fi
 
+# If --exclude-api-lists is not passed directly, exclude whitelist APIs.
+if [[ "$@" != "*--exclude-api-lists=*" ]]; then
+  extra_flags="${extra_flags} --exclude-api-lists=whitelist"
+fi
 
 ${ANDROID_HOST_OUT}/bin/veridex \
     --core-stubs=${PACKAGING}/core_dex_intermediates/classes.dex:${PACKAGING}/oahl_dex_intermediates/classes.dex \
diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc
index efb01f7..b21493f 100644
--- a/tools/veridex/hidden_api.cc
+++ b/tools/veridex/hidden_api.cc
@@ -24,7 +24,7 @@
 
 namespace art {
 
-HiddenApi::HiddenApi(const char* filename, bool sdk_uses_only) {
+HiddenApi::HiddenApi(const char* filename, const ApiListFilter& api_list_filter) {
   CHECK(filename != nullptr);
 
   std::ifstream in(filename);
@@ -37,9 +37,7 @@
     CHECK(success) << "Unknown ApiList flag: " << str;
     CHECK(membership.IsValid()) << "Invalid ApiList: " << membership;
 
-    if (sdk_uses_only != membership.Contains(hiddenapi::ApiList::Whitelist())) {
-      // Either we want only SDK uses and this is not a whitelist entry,
-      // or we want only non-SDK uses and this is a whitelist entry.
+    if (!api_list_filter.Matches(membership)) {
       continue;
     }
 
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index e1b67a2..3ac0125 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -17,6 +17,8 @@
 #ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_
 #define ART_TOOLS_VERIDEX_HIDDEN_API_H_
 
+#include "api_list_filter.h"
+
 #include "base/hiddenapi_flags.h"
 #include "dex/method_reference.h"
 
@@ -33,7 +35,7 @@
  */
 class HiddenApi {
  public:
-  HiddenApi(const char* flags_file, bool sdk_uses_only);
+  HiddenApi(const char* flags_file, const ApiListFilter& api_list_filter);
 
   hiddenapi::ApiList GetApiList(const std::string& name) const {
     auto it = api_list_.find(name);
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index 0253611..9079a1f 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -71,17 +71,17 @@
 static const char* kFlagsOption = "--api-flags=";
 static const char* kImprecise = "--imprecise";
 static const char* kTargetSdkVersion = "--target-sdk-version=";
-static const char* kOnlyReportSdkUses = "--only-report-sdk-uses";
 static const char* kAppClassFilter = "--app-class-filter=";
+static const char* kExcludeApiListsOption = "--exclude-api-lists=";
 
 struct VeridexOptions {
   const char* dex_file = nullptr;
   const char* core_stubs = nullptr;
   const char* flags_file = nullptr;
   bool precise = true;
-  int target_sdk_version = 28; /* P */
-  bool only_report_sdk_uses = false;
+  int target_sdk_version = 29; /* Q */
   std::vector<std::string> app_class_name_filter;
+  std::vector<std::string> exclude_api_lists;
 };
 
 static const char* Substr(const char* str, int index) {
@@ -108,13 +108,14 @@
       options->precise = false;
     } else if (StartsWith(argv[i], kTargetSdkVersion)) {
       options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion)));
-    } else if (strcmp(argv[i], kOnlyReportSdkUses) == 0) {
-      options->only_report_sdk_uses = true;
     } else if (StartsWith(argv[i], kAppClassFilter)) {
       options->app_class_name_filter = android::base::Split(
           Substr(argv[i], strlen(kAppClassFilter)), ",");
+    } else if (StartsWith(argv[i], kExcludeApiListsOption)) {
+      options->exclude_api_lists = android::base::Split(
+          Substr(argv[i], strlen(kExcludeApiListsOption)), ",");
     } else {
-      LOG(WARNING) << "Unknown command line argument: " << argv[i];
+      LOG(ERROR) << "Unknown command line argument: " << argv[i];
     }
   }
 }
@@ -228,9 +229,10 @@
     Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
 
     ClassFilter app_class_filter(options.app_class_name_filter);
+    ApiListFilter api_list_filter(options.exclude_api_lists);
 
     // Find and log uses of hidden APIs.
-    HiddenApi hidden_api(options.flags_file, options.only_report_sdk_uses);
+    HiddenApi hidden_api(options.flags_file, api_list_filter);
     HiddenApiStats stats;
 
     HiddenApiFinder api_finder(hidden_api);
@@ -243,7 +245,7 @@
       precise_api_finder.Dump(std::cout, &stats);
     }
 
-    DumpSummaryStats(std::cout, stats, options);
+    DumpSummaryStats(std::cout, stats, api_list_filter);
 
     if (options.precise) {
       std::cout << "To run an analysis that can give more reflection accesses, " << std::endl
@@ -256,20 +258,15 @@
  private:
   static void DumpSummaryStats(std::ostream& os,
                                const HiddenApiStats& stats,
-                               const VeridexOptions& options) {
+                               const ApiListFilter& api_list_filter) {
     static const char* kPrefix = "       ";
-    if (options.only_report_sdk_uses) {
-      os << stats.api_counts[hiddenapi::ApiList::Whitelist().GetIntValue()]
-         << " SDK API uses." << std::endl;
-    } else {
-      os << stats.count << " hidden API(s) used: "
-         << stats.linking_count << " linked against, "
-         << stats.reflection_count << " through reflection" << std::endl;
-      for (size_t i = 0; i < hiddenapi::ApiList::kValueCount; ++i) {
-        hiddenapi::ApiList api_list = hiddenapi::ApiList(i);
-        if (api_list != hiddenapi::ApiList::Whitelist()) {
-          os << kPrefix << stats.api_counts[i] << " in " << api_list << std::endl;
-        }
+    os << stats.count << " hidden API(s) used: "
+       << stats.linking_count << " linked against, "
+       << stats.reflection_count << " through reflection" << std::endl;
+    for (size_t i = 0; i < hiddenapi::ApiList::kValueCount; ++i) {
+      hiddenapi::ApiList api_list = hiddenapi::ApiList(i);
+      if (api_list_filter.Matches(api_list)) {
+        os << kPrefix << stats.api_counts[i] << " in " << api_list << std::endl;
       }
     }
   }