[veridex] Add a flow analysis pass to detect precise reflection usages.

bug: 77513322
Test: m

(cherry picked from commit 242758af3cf6eae389f43d3804acaabaa4ba93da)

Change-Id: Ifd9bed0819541ea88fc5363b5c7c2726972456ed
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp
index cbf62d9..570960c 100644
--- a/tools/veridex/Android.bp
+++ b/tools/veridex/Android.bp
@@ -16,8 +16,10 @@
     name: "veridex",
     host_supported: true,
     srcs: [
+        "flow_analysis.cc",
         "hidden_api.cc",
         "hidden_api_finder.cc",
+        "precise_hidden_api_finder.cc",
         "resolver.cc",
         "veridex.cc",
     ],
diff --git a/tools/veridex/README.md b/tools/veridex/README.md
index 0f91b08..f85a51b 100644
--- a/tools/veridex/README.md
+++ b/tools/veridex/README.md
@@ -11,4 +11,4 @@
 > make appcompat
 
 To run it:
-> ./art/tools/veridex/appcompat.sh test.apk
+> ./art/tools/veridex/appcompat.sh --dex-file=test.apk
diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh
index f75aa4f..31a8654 100755
--- a/tools/veridex/appcompat.sh
+++ b/tools/veridex/appcompat.sh
@@ -48,4 +48,4 @@
     --blacklist=${PACKAGING}/hiddenapi-blacklist.txt \
     --light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \
     --dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \
-    --dex-file=$1
+    $@
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
new file mode 100644
index 0000000..abd0b9b
--- /dev/null
+++ b/tools/veridex/flow_analysis.cc
@@ -0,0 +1,602 @@
+/*
+ * 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.
+ */
+
+#include "flow_analysis.h"
+
+#include "dex/bytecode_utils.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file_exception_helpers.h"
+#include "resolver.h"
+#include "veridex.h"
+
+namespace art {
+
+
+void VeriFlowAnalysis::SetAsBranchTarget(uint32_t dex_pc) {
+  if (dex_registers_[dex_pc] == nullptr) {
+    dex_registers_[dex_pc].reset(
+        new std::vector<RegisterValue>(code_item_accessor_.RegistersSize()));
+  }
+}
+
+bool VeriFlowAnalysis::IsBranchTarget(uint32_t dex_pc) {
+  return dex_registers_[dex_pc] != nullptr;
+}
+
+bool VeriFlowAnalysis::MergeRegisterValues(uint32_t dex_pc) {
+  // TODO: Do the merging. Right now, just return that we should continue
+  // the iteration if the instruction has not been visited.
+  return !instruction_infos_[dex_pc].has_been_visited;
+}
+
+void VeriFlowAnalysis::SetVisited(uint32_t dex_pc) {
+  instruction_infos_[dex_pc].has_been_visited = true;
+}
+
+void VeriFlowAnalysis::FindBranches() {
+  SetAsBranchTarget(0);
+
+  if (code_item_accessor_.TriesSize() != 0) {
+    // TODO: We need to mark the range of dex pcs as flowing in the handlers.
+    /*
+    for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) {
+      uint32_t dex_pc_start = try_item.start_addr_;
+      uint32_t dex_pc_end = dex_pc_start + try_item.insn_count_;
+    }
+    */
+
+    // Create branch targets for exception handlers.
+    const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData();
+    uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+    for (uint32_t idx = 0; idx < handlers_size; ++idx) {
+      CatchHandlerIterator iterator(handlers_ptr);
+      for (; iterator.HasNext(); iterator.Next()) {
+        SetAsBranchTarget(iterator.GetHandlerAddress());
+      }
+      handlers_ptr = iterator.EndDataPointer();
+    }
+  }
+
+  // Iterate over all instructions and find branching instructions.
+  for (const DexInstructionPcPair& pair : code_item_accessor_) {
+    const uint32_t dex_pc = pair.DexPc();
+    const Instruction& instruction = pair.Inst();
+
+    if (instruction.IsBranch()) {
+      SetAsBranchTarget(dex_pc + instruction.GetTargetOffset());
+    } else if (instruction.IsSwitch()) {
+      DexSwitchTable table(instruction, dex_pc);
+      for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) {
+        SetAsBranchTarget(dex_pc + s_it.CurrentTargetOffset());
+        if (table.ShouldBuildDecisionTree() && !s_it.IsLast()) {
+          SetAsBranchTarget(s_it.GetDexPcForCurrentIndex());
+        }
+      }
+    }
+  }
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register,
+                                      RegisterSource kind,
+                                      VeriClass* cls,
+                                      uint32_t source_id) {
+  current_registers_[dex_register] = RegisterValue(
+      kind, DexFileReference(&resolver_->GetDexFile(), source_id), cls);
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const RegisterValue& value) {
+  current_registers_[dex_register] = value;
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cls) {
+  current_registers_[dex_register] =
+      RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls);
+}
+
+const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) {
+  return current_registers_[dex_register];
+}
+
+RegisterValue VeriFlowAnalysis::GetReturnType(uint32_t method_index) {
+  const DexFile& dex_file = resolver_->GetDexFile();
+  const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index);
+  const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id);
+  VeriClass* cls = resolver_->GetVeriClass(proto_id.return_type_idx_);
+  return RegisterValue(RegisterSource::kMethod, DexFileReference(&dex_file, method_index), cls);
+}
+
+RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) {
+  const DexFile& dex_file = resolver_->GetDexFile();
+  const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+  VeriClass* cls = resolver_->GetVeriClass(field_id.type_idx_);
+  return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls);
+}
+
+void VeriFlowAnalysis::AnalyzeCode() {
+  std::vector<uint32_t> work_list;
+  work_list.push_back(0);
+  // Iterate over the code.
+  // When visiting unconditional branches (goto), move to that instruction.
+  // When visiting conditional branches, move to one destination, and put the other
+  // in the worklist.
+  while (!work_list.empty()) {
+    uint32_t dex_pc = work_list.back();
+    work_list.pop_back();
+    CHECK(IsBranchTarget(dex_pc));
+    current_registers_ = *dex_registers_[dex_pc].get();
+    while (true) {
+      const uint16_t* insns = code_item_accessor_.Insns() + dex_pc;
+      const Instruction& inst = *Instruction::At(insns);
+      ProcessDexInstruction(inst);
+      SetVisited(dex_pc);
+
+      int opcode_flags = Instruction::FlagsOf(inst.Opcode());
+      if ((opcode_flags & Instruction::kContinue) != 0) {
+        if ((opcode_flags & Instruction::kBranch) != 0) {
+          uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset();
+          if (MergeRegisterValues(branch_dex_pc)) {
+            work_list.push_back(branch_dex_pc);
+          }
+        }
+        dex_pc += inst.SizeInCodeUnits();
+      } else if ((opcode_flags & Instruction::kBranch) != 0) {
+        dex_pc += inst.GetTargetOffset();
+        DCHECK(IsBranchTarget(dex_pc));
+      } else {
+        break;
+      }
+
+      if (IsBranchTarget(dex_pc)) {
+        if (MergeRegisterValues(dex_pc)) {
+          current_registers_ = *dex_registers_[dex_pc].get();
+        } else {
+          break;
+        }
+      }
+    }
+  }
+}
+
+void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) {
+  switch (instruction.Opcode()) {
+    case Instruction::CONST_4:
+    case Instruction::CONST_16:
+    case Instruction::CONST:
+    case Instruction::CONST_HIGH16: {
+      int32_t register_index = instruction.VRegA();
+      UpdateRegister(register_index, VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::CONST_WIDE_16:
+    case Instruction::CONST_WIDE_32:
+    case Instruction::CONST_WIDE:
+    case Instruction::CONST_WIDE_HIGH16: {
+      int32_t register_index = instruction.VRegA();
+      UpdateRegister(register_index, VeriClass::long_);
+      break;
+    }
+
+    case Instruction::MOVE:
+    case Instruction::MOVE_FROM16:
+    case Instruction::MOVE_16: {
+      UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+      break;
+    }
+
+    case Instruction::MOVE_WIDE:
+    case Instruction::MOVE_WIDE_FROM16:
+    case Instruction::MOVE_WIDE_16: {
+      UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+      break;
+    }
+
+    case Instruction::MOVE_OBJECT:
+    case Instruction::MOVE_OBJECT_16:
+    case Instruction::MOVE_OBJECT_FROM16: {
+      UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+      break;
+    }
+    case Instruction::CONST_CLASS: {
+      UpdateRegister(instruction.VRegA_21c(),
+                     RegisterSource::kClass,
+                     VeriClass::class_,
+                     instruction.VRegB_21c());
+      break;
+    }
+    case Instruction::CONST_STRING: {
+      UpdateRegister(instruction.VRegA_21c(),
+                     RegisterSource::kString,
+                     VeriClass::string_,
+                     instruction.VRegB_21c());
+      break;
+    }
+
+    case Instruction::CONST_STRING_JUMBO: {
+      UpdateRegister(instruction.VRegA_31c(),
+                     RegisterSource::kString,
+                     VeriClass::string_,
+                     instruction.VRegB_31c());
+      break;
+    }
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_INTERFACE:
+    case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_SUPER:
+    case Instruction::INVOKE_VIRTUAL: {
+      VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c());
+      uint32_t args[5];
+      instruction.GetVarArgs(args);
+      if (method == VeriClass::forName_) {
+        RegisterValue value = GetRegister(args[0]);
+        last_result_ = RegisterValue(
+            value.GetSource(), value.GetDexFileReference(), VeriClass::class_);
+      } else if (IsGetField(method)) {
+        RegisterValue cls = GetRegister(args[0]);
+        RegisterValue name = GetRegister(args[1]);
+        field_uses_.push_back(std::make_pair(cls, name));
+        last_result_ = GetReturnType(instruction.VRegB_35c());
+      } else if (IsGetMethod(method)) {
+        RegisterValue cls = GetRegister(args[0]);
+        RegisterValue name = GetRegister(args[1]);
+        method_uses_.push_back(std::make_pair(cls, name));
+        last_result_ = GetReturnType(instruction.VRegB_35c());
+      } else if (method == VeriClass::getClass_) {
+        RegisterValue obj = GetRegister(args[0]);
+        last_result_ = RegisterValue(
+            obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_);
+      } else {
+        last_result_ = GetReturnType(instruction.VRegB_35c());
+      }
+      break;
+    }
+
+    case Instruction::INVOKE_DIRECT_RANGE:
+    case Instruction::INVOKE_INTERFACE_RANGE:
+    case Instruction::INVOKE_STATIC_RANGE:
+    case Instruction::INVOKE_SUPER_RANGE:
+    case Instruction::INVOKE_VIRTUAL_RANGE: {
+      last_result_ = GetReturnType(instruction.VRegB_3rc());
+      break;
+    }
+
+    case Instruction::MOVE_RESULT:
+    case Instruction::MOVE_RESULT_WIDE:
+    case Instruction::MOVE_RESULT_OBJECT: {
+      UpdateRegister(instruction.VRegA(), last_result_);
+      break;
+    }
+    case Instruction::RETURN_VOID:
+    case Instruction::RETURN_OBJECT:
+    case Instruction::RETURN_WIDE:
+    case Instruction::RETURN: {
+      break;
+    }
+    #define IF_XX(cond) \
+    case Instruction::IF_##cond: break; \
+    case Instruction::IF_##cond##Z: break
+
+    IF_XX(EQ);
+    IF_XX(NE);
+    IF_XX(LT);
+    IF_XX(LE);
+    IF_XX(GT);
+    IF_XX(GE);
+
+    case Instruction::GOTO:
+    case Instruction::GOTO_16:
+    case Instruction::GOTO_32: {
+      break;
+    }
+    case Instruction::INVOKE_POLYMORPHIC: {
+      // TODO
+      break;
+    }
+
+    case Instruction::INVOKE_POLYMORPHIC_RANGE: {
+      // TODO
+      break;
+    }
+
+    case Instruction::NEG_INT:
+    case Instruction::NEG_LONG:
+    case Instruction::NEG_FLOAT:
+    case Instruction::NEG_DOUBLE:
+    case Instruction::NOT_INT:
+    case Instruction::NOT_LONG: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::INT_TO_LONG:
+    case Instruction::INT_TO_FLOAT:
+    case Instruction::INT_TO_DOUBLE:
+    case Instruction::LONG_TO_INT:
+    case Instruction::LONG_TO_FLOAT:
+    case Instruction::LONG_TO_DOUBLE:
+    case Instruction::FLOAT_TO_INT:
+    case Instruction::FLOAT_TO_LONG:
+    case Instruction::FLOAT_TO_DOUBLE:
+    case Instruction::DOUBLE_TO_INT:
+    case Instruction::DOUBLE_TO_LONG:
+    case Instruction::DOUBLE_TO_FLOAT:
+    case Instruction::INT_TO_BYTE:
+    case Instruction::INT_TO_SHORT:
+    case Instruction::INT_TO_CHAR: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::ADD_INT:
+    case Instruction::ADD_LONG:
+    case Instruction::ADD_DOUBLE:
+    case Instruction::ADD_FLOAT:
+    case Instruction::SUB_INT:
+    case Instruction::SUB_LONG:
+    case Instruction::SUB_FLOAT:
+    case Instruction::SUB_DOUBLE:
+    case Instruction::MUL_INT:
+    case Instruction::MUL_LONG:
+    case Instruction::MUL_FLOAT:
+    case Instruction::MUL_DOUBLE:
+    case Instruction::DIV_INT:
+    case Instruction::DIV_LONG:
+    case Instruction::DIV_FLOAT:
+    case Instruction::DIV_DOUBLE:
+    case Instruction::REM_INT:
+    case Instruction::REM_LONG:
+    case Instruction::REM_FLOAT:
+    case Instruction::REM_DOUBLE:
+    case Instruction::AND_INT:
+    case Instruction::AND_LONG:
+    case Instruction::SHL_INT:
+    case Instruction::SHL_LONG:
+    case Instruction::SHR_INT:
+    case Instruction::SHR_LONG:
+    case Instruction::USHR_INT:
+    case Instruction::USHR_LONG:
+    case Instruction::OR_INT:
+    case Instruction::OR_LONG:
+    case Instruction::XOR_INT:
+    case Instruction::XOR_LONG: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::ADD_INT_2ADDR:
+    case Instruction::ADD_LONG_2ADDR:
+    case Instruction::ADD_DOUBLE_2ADDR:
+    case Instruction::ADD_FLOAT_2ADDR:
+    case Instruction::SUB_INT_2ADDR:
+    case Instruction::SUB_LONG_2ADDR:
+    case Instruction::SUB_FLOAT_2ADDR:
+    case Instruction::SUB_DOUBLE_2ADDR:
+    case Instruction::MUL_INT_2ADDR:
+    case Instruction::MUL_LONG_2ADDR:
+    case Instruction::MUL_FLOAT_2ADDR:
+    case Instruction::MUL_DOUBLE_2ADDR:
+    case Instruction::DIV_INT_2ADDR:
+    case Instruction::DIV_LONG_2ADDR:
+    case Instruction::REM_INT_2ADDR:
+    case Instruction::REM_LONG_2ADDR:
+    case Instruction::REM_FLOAT_2ADDR:
+    case Instruction::REM_DOUBLE_2ADDR:
+    case Instruction::SHL_INT_2ADDR:
+    case Instruction::SHL_LONG_2ADDR:
+    case Instruction::SHR_INT_2ADDR:
+    case Instruction::SHR_LONG_2ADDR:
+    case Instruction::USHR_INT_2ADDR:
+    case Instruction::USHR_LONG_2ADDR:
+    case Instruction::DIV_FLOAT_2ADDR:
+    case Instruction::DIV_DOUBLE_2ADDR:
+    case Instruction::AND_INT_2ADDR:
+    case Instruction::AND_LONG_2ADDR:
+    case Instruction::OR_INT_2ADDR:
+    case Instruction::OR_LONG_2ADDR:
+    case Instruction::XOR_INT_2ADDR:
+    case Instruction::XOR_LONG_2ADDR: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::ADD_INT_LIT16:
+    case Instruction::AND_INT_LIT16:
+    case Instruction::OR_INT_LIT16:
+    case Instruction::XOR_INT_LIT16:
+    case Instruction::RSUB_INT:
+    case Instruction::MUL_INT_LIT16:
+    case Instruction::DIV_INT_LIT16:
+    case Instruction::REM_INT_LIT16: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::ADD_INT_LIT8:
+    case Instruction::AND_INT_LIT8:
+    case Instruction::OR_INT_LIT8:
+    case Instruction::XOR_INT_LIT8:
+    case Instruction::RSUB_INT_LIT8:
+    case Instruction::MUL_INT_LIT8:
+    case Instruction::DIV_INT_LIT8:
+    case Instruction::REM_INT_LIT8:
+    case Instruction::SHL_INT_LIT8:
+    case Instruction::SHR_INT_LIT8: {
+    case Instruction::USHR_INT_LIT8: {
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::NEW_INSTANCE: {
+      VeriClass* cls = resolver_->GetVeriClass(dex::TypeIndex(instruction.VRegB_21c()));
+      UpdateRegister(instruction.VRegA(), cls);
+      break;
+    }
+
+    case Instruction::NEW_ARRAY: {
+      dex::TypeIndex type_index(instruction.VRegC_22c());
+      VeriClass* cls = resolver_->GetVeriClass(type_index);
+      UpdateRegister(instruction.VRegA_22c(), cls);
+      break;
+    }
+
+    case Instruction::FILLED_NEW_ARRAY: {
+      dex::TypeIndex type_index(instruction.VRegB_35c());
+      VeriClass* cls = resolver_->GetVeriClass(type_index);
+      UpdateRegister(instruction.VRegA_22c(), cls);
+      break;
+    }
+
+    case Instruction::FILLED_NEW_ARRAY_RANGE: {
+      dex::TypeIndex type_index(instruction.VRegB_3rc());
+      uint32_t register_index = instruction.VRegC_3rc();
+      VeriClass* cls = resolver_->GetVeriClass(type_index);
+      UpdateRegister(register_index, cls);
+      break;
+    }
+
+    case Instruction::FILL_ARRAY_DATA: {
+      break;
+    }
+
+    case Instruction::CMP_LONG:
+    case Instruction::CMPG_FLOAT:
+    case Instruction::CMPG_DOUBLE:
+    case Instruction::CMPL_FLOAT:
+    case Instruction::CMPL_DOUBLE:
+      UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::NOP:
+      break;
+
+    case Instruction::IGET:
+    case Instruction::IGET_WIDE:
+    case Instruction::IGET_OBJECT:
+    case Instruction::IGET_BOOLEAN:
+    case Instruction::IGET_BYTE:
+    case Instruction::IGET_CHAR:
+    case Instruction::IGET_SHORT: {
+      UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+      break;
+    }
+
+    case Instruction::IPUT:
+    case Instruction::IPUT_WIDE:
+    case Instruction::IPUT_OBJECT:
+    case Instruction::IPUT_BOOLEAN:
+    case Instruction::IPUT_BYTE:
+    case Instruction::IPUT_CHAR:
+    case Instruction::IPUT_SHORT: {
+      break;
+    }
+
+    case Instruction::SGET:
+    case Instruction::SGET_WIDE:
+    case Instruction::SGET_OBJECT:
+    case Instruction::SGET_BOOLEAN:
+    case Instruction::SGET_BYTE:
+    case Instruction::SGET_CHAR:
+    case Instruction::SGET_SHORT: {
+      UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+      break;
+    }
+
+    case Instruction::SPUT:
+    case Instruction::SPUT_WIDE:
+    case Instruction::SPUT_OBJECT:
+    case Instruction::SPUT_BOOLEAN:
+    case Instruction::SPUT_BYTE:
+    case Instruction::SPUT_CHAR:
+    case Instruction::SPUT_SHORT: {
+      break;
+    }
+
+#define ARRAY_XX(kind, anticipated_type)                                          \
+    case Instruction::AGET##kind: {                                               \
+      UpdateRegister(instruction.VRegA_23x(), anticipated_type);                  \
+      break;                                                                      \
+    }                                                                             \
+    case Instruction::APUT##kind: {                                               \
+      break;                                                                      \
+    }
+
+    ARRAY_XX(, VeriClass::integer_);
+    ARRAY_XX(_WIDE, VeriClass::long_);
+    ARRAY_XX(_BOOLEAN, VeriClass::boolean_);
+    ARRAY_XX(_BYTE, VeriClass::byte_);
+    ARRAY_XX(_CHAR, VeriClass::char_);
+    ARRAY_XX(_SHORT, VeriClass::short_);
+
+    case Instruction::AGET_OBJECT: {
+      // TODO: take the component type.
+      UpdateRegister(instruction.VRegA_23x(), VeriClass::object_);
+      break;
+    }
+
+    case Instruction::APUT_OBJECT: {
+      break;
+    }
+
+    case Instruction::ARRAY_LENGTH: {
+      UpdateRegister(instruction.VRegA_12x(), VeriClass::integer_);
+      break;
+    }
+
+    case Instruction::MOVE_EXCEPTION: {
+      UpdateRegister(instruction.VRegA_11x(), VeriClass::throwable_);
+      break;
+    }
+
+    case Instruction::THROW: {
+      break;
+    }
+
+    case Instruction::INSTANCE_OF: {
+      uint8_t destination = instruction.VRegA_22c();
+      UpdateRegister(destination, VeriClass::boolean_);
+      break;
+    }
+
+    case Instruction::CHECK_CAST: {
+      uint8_t reference = instruction.VRegA_21c();
+      dex::TypeIndex type_index(instruction.VRegB_21c());
+      UpdateRegister(reference, resolver_->GetVeriClass(type_index));
+      break;
+    }
+
+    case Instruction::MONITOR_ENTER:
+    case Instruction::MONITOR_EXIT: {
+      break;
+    }
+
+    case Instruction::SPARSE_SWITCH:
+    case Instruction::PACKED_SWITCH:
+      break;
+
+    default:
+      break;
+  }
+}
+
+void VeriFlowAnalysis::Run() {
+  FindBranches();
+  AnalyzeCode();
+}
+
+}  // namespace art
diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h
new file mode 100644
index 0000000..c065fb8
--- /dev/null
+++ b/tools/veridex/flow_analysis.h
@@ -0,0 +1,147 @@
+/*
+ * 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_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
+#define ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
+
+#include "dex/code_item_accessors.h"
+#include "dex/dex_file_reference.h"
+#include "dex/method_reference.h"
+#include "veridex.h"
+
+namespace art {
+
+class VeridexClass;
+class VeridexResolver;
+
+/**
+ * The source where a dex register comes from.
+ */
+enum class RegisterSource {
+  kParameter,
+  kField,
+  kMethod,
+  kClass,
+  kString,
+  kNone
+};
+
+/**
+ * Abstract representation of a dex register value.
+ */
+class RegisterValue {
+ public:
+  RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {}
+  RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type)
+      : source_(source), reference_(reference), type_(type) {}
+
+  RegisterSource GetSource() const { return source_; }
+  DexFileReference GetDexFileReference() const { return reference_; }
+  const VeriClass* GetType() const { return type_; }
+
+  const char* ToString() const {
+    switch (source_) {
+      case RegisterSource::kString:
+        return reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index));
+      case RegisterSource::kClass:
+        return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index));
+      default:
+        return "<unknown>";
+    }
+  }
+
+ private:
+  RegisterSource source_;
+  DexFileReference reference_;
+  const VeriClass* type_;
+};
+
+struct InstructionInfo {
+  bool has_been_visited;
+};
+
+class VeriFlowAnalysis {
+ public:
+  VeriFlowAnalysis(VeridexResolver* resolver,
+                   const CodeItemDataAccessor& code_item_accessor)
+      : resolver_(resolver),
+        code_item_accessor_(code_item_accessor),
+        dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()),
+        instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {}
+
+  void Run();
+
+  const std::vector<std::pair<RegisterValue, RegisterValue>>& GetFieldUses() const {
+    return field_uses_;
+  }
+
+  const std::vector<std::pair<RegisterValue, RegisterValue>>& GetMethodUses() const {
+    return method_uses_;
+  }
+
+ private:
+  // Find all branches in the code.
+  void FindBranches();
+
+  // Analyze all non-deead instructions in the code.
+  void AnalyzeCode();
+
+  // Set the instruction at the given pc as a branch target.
+  void SetAsBranchTarget(uint32_t dex_pc);
+
+  // Whether the instruction at the given pc is a branch target.
+  bool IsBranchTarget(uint32_t dex_pc);
+
+  // Merge the register values at the given pc with `current_registers`.
+  // Return whether the register values have changed, and the instruction needs
+  // to be visited again.
+  bool MergeRegisterValues(uint32_t dex_pc);
+
+  void UpdateRegister(
+      uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id);
+  void UpdateRegister(uint32_t dex_register, const RegisterValue& value);
+  void UpdateRegister(uint32_t dex_register, const VeriClass* cls);
+  const RegisterValue& GetRegister(uint32_t dex_register);
+  void ProcessDexInstruction(const Instruction& inst);
+  void SetVisited(uint32_t dex_pc);
+  RegisterValue GetReturnType(uint32_t method_index);
+  RegisterValue GetFieldType(uint32_t field_index);
+
+  VeridexResolver* resolver_;
+  const CodeItemDataAccessor& code_item_accessor_;
+
+  // Vector of register values for all branch targets.
+  std::vector<std::unique_ptr<std::vector<RegisterValue>>> dex_registers_;
+
+  // The current values of dex registers.
+  std::vector<RegisterValue> current_registers_;
+
+  // Information on each instruction useful for the analysis.
+  std::vector<InstructionInfo> instruction_infos_;
+
+  // The value of invoke instructions, to be fetched when visiting move-result.
+  RegisterValue last_result_;
+
+  // List of reflection field uses found.
+  std::vector<std::pair<RegisterValue, RegisterValue>> field_uses_;
+
+  // List of reflection method uses found.
+  std::vector<std::pair<RegisterValue, RegisterValue>> method_uses_;
+};
+
+}  // namespace art
+
+#endif  // ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index 5893b8a..4c67768 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -18,6 +18,7 @@
 #define ART_TOOLS_VERIDEX_HIDDEN_API_H_
 
 #include "dex/hidden_api_access_flags.h"
+#include "dex/method_reference.h"
 
 #include <ostream>
 #include <set>
@@ -58,6 +59,10 @@
 
   static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index);
 
+  static std::string GetApiMethodName(MethodReference ref) {
+    return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index);
+  }
+
  private:
   static bool IsInList(const std::string& name, const std::set<std::string>& list) {
     return list.find(name) != list.end();
@@ -70,6 +75,13 @@
   std::set<std::string> dark_greylist_;
 };
 
+struct HiddenApiStats {
+  uint32_t count = 0;
+  uint32_t reflection_count = 0;
+  uint32_t linking_count = 0;
+  uint32_t api_counts[4] = { 0, 0, 0, 0 };
+};
+
 }  // namespace art
 
 #endif  // ART_TOOLS_VERIDEX_HIDDEN_API_H_
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
index 4885e02..b9be618 100644
--- a/tools/veridex/hidden_api_finder.cc
+++ b/tools/veridex/hidden_api_finder.cc
@@ -193,32 +193,26 @@
   }
 }
 
-static std::string GetApiMethodName(MethodReference ref) {
-  return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index);
-}
-
 void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
   for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
     CollectAccesses(resolver.get());
   }
-
-  Dump(std::cout);
 }
 
-void HiddenApiFinder::Dump(std::ostream& os) {
+void HiddenApiFinder::Dump(std::ostream& os,
+                           HiddenApiStats* stats,
+                           bool dump_reflection) {
   static const char* kPrefix = "       ";
-  uint32_t count = 0;
-  uint32_t linking_count = method_locations_.size() + field_locations_.size();
-  uint32_t api_counts[4] = {0, 0, 0, 0};
+  stats->linking_count = method_locations_.size() + field_locations_.size();
 
   // Dump methods from hidden APIs linked against.
   for (const std::pair<std::string, std::vector<MethodReference>>& pair : method_locations_) {
     HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
-    api_counts[api_list]++;
-    os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):";
+    stats->api_counts[api_list]++;
+    os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
     os << std::endl;
     for (const MethodReference& ref : pair.second) {
-      os << kPrefix << GetApiMethodName(ref) << std::endl;
+      os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
     }
     os << std::endl;
   }
@@ -226,42 +220,35 @@
   // Dump fields from hidden APIs linked against.
   for (const std::pair<std::string, std::vector<MethodReference>>& pair : field_locations_) {
     HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
-    api_counts[api_list]++;
-    os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):";
+    stats->api_counts[api_list]++;
+    os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
     os << std::endl;
     for (const MethodReference& ref : pair.second) {
-      os << kPrefix << GetApiMethodName(ref) << std::endl;
+      os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
     }
     os << std::endl;
   }
 
-  // Dump potential reflection uses.
-  for (const std::string& cls : classes_) {
-    for (const std::string& name : strings_) {
-      std::string full_name = cls + "->" + name;
-      HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
-      api_counts[api_list]++;
-      if (api_list != HiddenApiAccessFlags::kWhitelist) {
-        os << "#" << ++count << ": Reflection " << api_list << " " << full_name
-           << " potential use(s):";
-        os << std::endl;
-        for (const MethodReference& ref : reflection_locations_[name]) {
-          os << kPrefix << GetApiMethodName(ref) << std::endl;
+  if (dump_reflection) {
+    // Dump potential reflection uses.
+    for (const std::string& cls : classes_) {
+      for (const std::string& name : strings_) {
+        std::string full_name = cls + "->" + name;
+        HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+        stats->api_counts[api_list]++;
+        if (api_list != HiddenApiAccessFlags::kWhitelist) {
+          stats->reflection_count++;
+          os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
+             << " potential use(s):";
+          os << std::endl;
+          for (const MethodReference& ref : reflection_locations_[name]) {
+            os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+          }
+          os << std::endl;
         }
-        os << std::endl;
       }
     }
   }
-
-  os << count << " hidden API(s) used: "
-     << linking_count << " linked against, "
-     << count - linking_count << " potentially through reflection" << std::endl;
-  os << kPrefix << api_counts[HiddenApiAccessFlags::kBlacklist]
-     << " in blacklist" << std::endl;
-  os << kPrefix << api_counts[HiddenApiAccessFlags::kDarkGreylist]
-     << " in dark greylist" << std::endl;
-  os << kPrefix << api_counts[HiddenApiAccessFlags::kLightGreylist]
-     << " in light greylist" << std::endl;
 }
 
 }  // namespace art
diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h
index 243079c..f7d3dc8 100644
--- a/tools/veridex/hidden_api_finder.h
+++ b/tools/veridex/hidden_api_finder.h
@@ -27,6 +27,7 @@
 namespace art {
 
 class HiddenApi;
+struct HiddenApiStats;
 class VeridexResolver;
 
 /**
@@ -40,11 +41,12 @@
   // hidden API uses.
   void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers);
 
+  void Dump(std::ostream& os, HiddenApiStats* stats, bool dump_reflection);
+
  private:
   void CollectAccesses(VeridexResolver* resolver);
   void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref);
   void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref);
-  void Dump(std::ostream& os);
 
   const HiddenApi& hidden_api_;
   std::set<std::string> classes_;
diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
new file mode 100644
index 0000000..2092af3
--- /dev/null
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include "precise_hidden_api_finder.h"
+
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file.h"
+#include "dex/method_reference.h"
+#include "flow_analysis.h"
+#include "hidden_api.h"
+#include "resolver.h"
+#include "veridex.h"
+
+#include <iostream>
+
+namespace art {
+
+void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+  for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
+    const DexFile& dex_file = resolver->GetDexFile();
+    size_t class_def_count = dex_file.NumClassDefs();
+    for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+      const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+      const uint8_t* class_data = dex_file.GetClassData(class_def);
+      if (class_data == nullptr) {
+        // Empty class.
+        continue;
+      }
+      ClassDataItemIterator it(dex_file, class_data);
+      it.SkipAllFields();
+      for (; it.HasNextMethod(); it.Next()) {
+        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+        if (code_item == nullptr) {
+          continue;
+        }
+        CodeItemDataAccessor code_item_accessor(dex_file, code_item);
+        VeriFlowAnalysis ana(resolver.get(), code_item_accessor);
+        ana.Run();
+        if (!ana.GetFieldUses().empty()) {
+          field_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetFieldUses();
+        }
+        if (!ana.GetMethodUses().empty()) {
+          method_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetMethodUses();
+        }
+      }
+    }
+  }
+}
+
+void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) {
+  static const char* kPrefix = "       ";
+  for (auto kinds : { field_uses_, method_uses_ }) {
+    for (auto it : kinds) {
+      MethodReference ref = it.first;
+      for (const std::pair<RegisterValue, RegisterValue>& info : it.second) {
+        if ((info.first.GetSource() == RegisterSource::kClass ||
+             info.first.GetSource() == RegisterSource::kString) &&
+            info.second.GetSource() == RegisterSource::kString) {
+          std::string cls(info.first.ToString());
+          std::string name(info.second.ToString());
+          std::string full_name = cls + "->" + name;
+          HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+          stats->api_counts[api_list]++;
+          if (api_list != HiddenApiAccessFlags::kWhitelist) {
+            ++stats->reflection_count;
+            os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
+               << " use:";
+            os << std::endl;
+            os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+            os << std::endl;
+          }
+        }
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h
new file mode 100644
index 0000000..22744a6
--- /dev/null
+++ b/tools/veridex/precise_hidden_api_finder.h
@@ -0,0 +1,55 @@
+/*
+ * 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_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
+#define ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
+
+#include "dex/method_reference.h"
+#include "flow_analysis.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+namespace art {
+
+class HiddenApi;
+struct HiddenApiStats;
+class VeridexResolver;
+
+/**
+ * Reports known uses of hidden APIs from reflection.
+ */
+class PreciseHiddenApiFinder {
+ public:
+  explicit PreciseHiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {}
+
+  // Iterate over the dex files associated with the passed resolvers to report
+  // hidden API uses.
+  void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers);
+
+  void Dump(std::ostream& os, HiddenApiStats* stats);
+
+ private:
+  const HiddenApi& hidden_api_;
+  std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> field_uses_;
+  std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> method_uses_;
+};
+
+}  // namespace art
+
+#endif  // ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc
index 13dda5c..9113039 100644
--- a/tools/veridex/resolver.cc
+++ b/tools/veridex/resolver.cc
@@ -59,6 +59,14 @@
 static bool HasSameNameAndSignature(const DexFile& dex_file,
                                     const DexFile::MethodId& method_id,
                                     const char* method_name,
+                                    const char* type) {
+  return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
+      strcmp(type, dex_file.GetMethodSignature(method_id).ToString().c_str()) == 0;
+}
+
+static bool HasSameNameAndSignature(const DexFile& dex_file,
+                                    const DexFile::MethodId& method_id,
+                                    const char* method_name,
                                     const Signature& signature) {
   return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
       dex_file.GetMethodSignature(method_id) == signature;
@@ -241,6 +249,34 @@
   return nullptr;
 }
 
+VeriMethod VeridexResolver::LookupDeclaredMethodIn(const VeriClass& kls,
+                                                   const char* method_name,
+                                                   const char* type) const {
+  if (kls.IsPrimitive()) {
+    return nullptr;
+  }
+  if (kls.IsArray()) {
+    return nullptr;
+  }
+  VeridexResolver* resolver = GetResolverOf(kls);
+  const DexFile& other_dex_file = resolver->dex_file_;
+  const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef());
+  if (class_data != nullptr) {
+    ClassDataItemIterator it(other_dex_file, class_data);
+    it.SkipAllFields();
+    for (; it.HasNextMethod(); it.Next()) {
+      const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex());
+      if (HasSameNameAndSignature(other_dex_file,
+                                  other_method_id,
+                                  method_name,
+                                  type)) {
+        return it.DataPointer();
+      }
+    }
+  }
+  return nullptr;
+}
+
 VeriMethod VeridexResolver::GetMethod(uint32_t method_index) {
   VeriMethod method_info = method_infos_[method_index];
   if (method_info == nullptr) {
diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h
index 06c8aa7..52b15fe 100644
--- a/tools/veridex/resolver.h
+++ b/tools/veridex/resolver.h
@@ -66,6 +66,11 @@
                           const char* field_name,
                           const char* field_type);
 
+  // Lookup a method declared in `kls`.
+  VeriMethod LookupDeclaredMethodIn(const VeriClass& kls,
+                                    const char* method_name,
+                                    const char* signature) const;
+
   // Resolve all type_id/method_id/field_id.
   void ResolveAll();
 
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index 16e9f0e..6e72faa 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -22,6 +22,7 @@
 #include "dex/dex_file_loader.h"
 #include "hidden_api.h"
 #include "hidden_api_finder.h"
+#include "precise_hidden_api_finder.h"
 #include "resolver.h"
 
 #include <sstream>
@@ -47,8 +48,18 @@
 VeriClass* VeriClass::double_ = &d_;
 VeriClass* VeriClass::long_ = &j_;
 VeriClass* VeriClass::void_ = &v_;
+
 // Will be set after boot classpath has been resolved.
 VeriClass* VeriClass::object_ = nullptr;
+VeriClass* VeriClass::class_ = nullptr;
+VeriClass* VeriClass::string_ = nullptr;
+VeriClass* VeriClass::throwable_ = nullptr;
+VeriMethod VeriClass::forName_ = nullptr;
+VeriMethod VeriClass::getField_ = nullptr;
+VeriMethod VeriClass::getDeclaredField_ = nullptr;
+VeriMethod VeriClass::getMethod_ = nullptr;
+VeriMethod VeriClass::getDeclaredMethod_ = nullptr;
+VeriMethod VeriClass::getClass_ = nullptr;
 
 struct VeridexOptions {
   const char* dex_file = nullptr;
@@ -56,6 +67,7 @@
   const char* blacklist = nullptr;
   const char* light_greylist = nullptr;
   const char* dark_greylist = nullptr;
+  bool precise = true;
 };
 
 static const char* Substr(const char* str, int index) {
@@ -76,6 +88,7 @@
   static const char* kBlacklistOption = "--blacklist=";
   static const char* kDarkGreylistOption = "--dark-greylist=";
   static const char* kLightGreylistOption = "--light-greylist=";
+  static const char* kImprecise = "--imprecise";
 
   for (int i = 0; i < argc; ++i) {
     if (StartsWith(argv[i], kDexFileOption)) {
@@ -88,6 +101,8 @@
       options->dark_greylist = Substr(argv[i], strlen(kDarkGreylistOption));
     } else if (StartsWith(argv[i], kLightGreylistOption)) {
       options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption));
+    } else if (strcmp(argv[i], kImprecise) == 0) {
+      options->precise = false;
     }
   }
 }
@@ -157,21 +172,70 @@
     std::vector<std::unique_ptr<VeridexResolver>> boot_resolvers;
     Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers);
 
-    // Now that boot classpath has been resolved, fill j.l.Object.
+    // Now that boot classpath has been resolved, fill classes and reflection
+    // methods.
     VeriClass::object_ = type_map["Ljava/lang/Object;"];
+    VeriClass::class_ = type_map["Ljava/lang/Class;"];
+    VeriClass::string_ = type_map["Ljava/lang/String;"];
+    VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"];
+    VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+    VeriClass::getField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
+    VeriClass::getDeclaredField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
+    VeriClass::getMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_,
+        "getMethod",
+        "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+    VeriClass::getDeclaredMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::class_,
+        "getDeclaredMethod",
+        "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+    VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+        *VeriClass::object_, "getClass", "()Ljava/lang/Class;");
 
     std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
     Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
 
     // Find and log uses of hidden APIs.
     HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist);
+    HiddenApiStats stats;
+
     HiddenApiFinder api_finder(hidden_api);
     api_finder.Run(app_resolvers);
+    api_finder.Dump(std::cout, &stats, !options.precise);
+
+    if (options.precise) {
+      PreciseHiddenApiFinder precise_api_finder(hidden_api);
+      precise_api_finder.Run(app_resolvers);
+      precise_api_finder.Dump(std::cout, &stats);
+    }
+
+    DumpSummaryStats(std::cout, stats);
+
+    if (options.precise) {
+      std::cout << "To run an analysis that can give more reflection accesses, " << std::endl
+                << "but could include false positives, pass the --imprecise flag. " << std::endl;
+    }
 
     return 0;
   }
 
  private:
+  static void DumpSummaryStats(std::ostream& os, const HiddenApiStats& stats) {
+    static const char* kPrefix = "       ";
+    os << stats.count << " hidden API(s) used: "
+       << stats.linking_count << " linked against, "
+       << stats.reflection_count << " through reflection" << std::endl;
+    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist]
+       << " in blacklist" << std::endl;
+    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist]
+       << " in dark greylist" << std::endl;
+    os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist]
+       << " in light greylist" << std::endl;
+  }
+
   static bool Load(const std::string& filename,
                    std::string& content,
                    std::vector<std::unique_ptr<const DexFile>>* dex_files,
diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h
index 0c928ab..75e4845 100644
--- a/tools/veridex/veridex.h
+++ b/tools/veridex/veridex.h
@@ -25,6 +25,18 @@
 namespace art {
 
 /**
+ * Abstraction for fields defined in dex files. Currently, that's a pointer into their
+ * `encoded_field` description.
+ */
+using VeriField = const uint8_t*;
+
+/**
+ * Abstraction for methods defined in dex files. Currently, that's a pointer into their
+ * `encoded_method` description.
+ */
+using VeriMethod = const uint8_t*;
+
+/**
  * Abstraction for classes defined, or implicitly defined (for arrays and primitives)
  * in dex files.
  */
@@ -52,6 +64,9 @@
   const DexFile::ClassDef* GetClassDef() const { return class_def_; }
 
   static VeriClass* object_;
+  static VeriClass* class_;
+  static VeriClass* string_;
+  static VeriClass* throwable_;
   static VeriClass* boolean_;
   static VeriClass* byte_;
   static VeriClass* char_;
@@ -62,23 +77,26 @@
   static VeriClass* long_;
   static VeriClass* void_;
 
+  static VeriMethod forName_;
+  static VeriMethod getField_;
+  static VeriMethod getDeclaredField_;
+  static VeriMethod getMethod_;
+  static VeriMethod getDeclaredMethod_;
+  static VeriMethod getClass_;
+
  private:
   Primitive::Type kind_;
   uint8_t dimensions_;
   const DexFile::ClassDef* class_def_;
 };
 
-/**
- * Abstraction for fields defined in dex files. Currently, that's a pointer into their
- * `encoded_field` description.
- */
-using VeriField = const uint8_t*;
+inline bool IsGetMethod(VeriMethod method) {
+  return method == VeriClass::getMethod_ || method == VeriClass::getDeclaredMethod_;
+}
 
-/**
- * Abstraction for methods defined in dex files. Currently, that's a pointer into their
- * `encoded_method` description.
- */
-using VeriMethod = const uint8_t*;
+inline bool IsGetField(VeriMethod method) {
+  return method == VeriClass::getField_ || method == VeriClass::getDeclaredField_;
+}
 
 /**
  * Map from name to VeriClass to quickly lookup classes.