apilint: Fix API lint issues

Fixes a bug where only the name instead of the fully qualified name was
considered when looking for a class, which lead to faulty results for inner
classes.

Test: python tools/apilint/apilint_test.py
Change-Id: Ib015669ed3faef21d2bdd16f1e27bc55c8669d70
(cherry picked from commit 2c5cacfd36128f43f5fab4f0665acf69ac049a44)
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index b5a990e..1eec218 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -267,6 +267,18 @@
                         raise TypeError("send() must be followed by next(), not send()")
 
 
+def _retry_iterator(it):
+    """Wraps an iterator, such that calling send(True) on it will redeliver the same element"""
+    for e in it:
+        while True:
+            retry = yield e
+            if not retry:
+                break
+            # send() was called, asking us to redeliver clazz on next(). Still need to yield
+            # a dummy value to the send() first though.
+            if (yield "Returning clazz on next()"):
+                raise TypeError("send() must be followed by next(), not send()")
+
 def _parse_to_matching_class(classes, needle):
     """Takes a classes generator and parses it until it returns the class we're looking for
 
@@ -276,8 +288,8 @@
         if clazz.pkg.name < needle.pkg.name:
             # We haven't reached the right package yet
             continue
-        if clazz.name < needle.name:
-            # We haven't reached the right class yet
+        if clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname:
+            # We're in the right package, but not the right class yet
             continue
         if clazz.fullname == needle.fullname:
             return clazz
diff --git a/tools/apilint/apilint_sha_system.sh b/tools/apilint/apilint_sha_system.sh
new file mode 100755
index 0000000..8538a3d
--- /dev/null
+++ b/tools/apilint/apilint_sha_system.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# 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.
+
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+  python tools/apilint/apilint.py \
+    --base-current <(git show $1:api/current.txt) \
+    --base-previous <(git show $1^:api/current.txt) \
+    <(git show $1:api/system-current.txt) \
+    <(git show $1^:api/system-current.txt)
+fi
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
new file mode 100644
index 0000000..59fc14d
--- /dev/null
+++ b/tools/apilint/apilint_test.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+
+# 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.
+
+import unittest
+
+import apilint
+
+def cls(pkg, name):
+    return apilint.Class(apilint.Package(999, "package %s {" % pkg, None), 999,
+                  "public final class %s {" % name, None)
+
+_ri = apilint._retry_iterator
+
+c1 = cls("android.app", "ActivityManager")
+c2 = cls("android.app", "Notification")
+c3 = cls("android.app", "Notification.Action")
+c4 = cls("android.graphics", "Bitmap")
+
+class UtilTests(unittest.TestCase):
+    def test_retry_iterator(self):
+        it = apilint._retry_iterator([1, 2, 3, 4])
+        self.assertEqual(it.next(), 1)
+        self.assertEqual(it.next(), 2)
+        self.assertEqual(it.next(), 3)
+        it.send("retry")
+        self.assertEqual(it.next(), 3)
+        self.assertEqual(it.next(), 4)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_retry_iterator_one(self):
+        it = apilint._retry_iterator([1])
+        self.assertEqual(it.next(), 1)
+        it.send("retry")
+        self.assertEqual(it.next(), 1)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_retry_iterator_one(self):
+        it = apilint._retry_iterator([1])
+        self.assertEqual(it.next(), 1)
+        it.send("retry")
+        self.assertEqual(it.next(), 1)
+        with self.assertRaises(StopIteration):
+            it.next()
+
+    def test_skip_to_matching_class_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(apilint._parse_to_matching_class(it, c3),
+                          c3)
+        self.assertEqual(it.next(), c4)
+
+    def test_skip_to_matching_class_not_found(self):
+        it = _ri([c1, c2, c3, c4])
+        self.assertEquals(apilint._parse_to_matching_class(it, cls("android.content", "ContentProvider")),
+                          None)
+        self.assertEqual(it.next(), c4)
+
+if __name__ == "__main__":
+    unittest.main()
\ No newline at end of file