apilint.py revision 294f0de15b510afc06a436bf7cd45d99512c71d3
18190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#!/usr/bin/env python
28190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
38190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Copyright (C) 2014 The Android Open Source Project
48190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
58190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Licensed under the Apache License, Version 2.0 (the 'License');
68190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# you may not use this file except in compliance with the License.
78190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# You may obtain a copy of the License at
88190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
98190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#      http://www.apache.org/licenses/LICENSE-2.0
108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey#
118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# Unless required by applicable law or agreed to in writing, software
128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# distributed under the License is distributed on an 'AS IS' BASIS,
138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# See the License for the specific language governing permissions and
158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey# limitations under the License.
168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey"""
188190f4885b3eb34231877003a583116a0e82826eJeff SharkeyEnforces common Android public API design patterns.  It ignores lint messages from
198190f4885b3eb34231877003a583116a0e82826eJeff Sharkeya previous API level, if provided.
208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
218190f4885b3eb34231877003a583116a0e82826eJeff SharkeyUsage: apilint.py current.txt
228190f4885b3eb34231877003a583116a0e82826eJeff SharkeyUsage: apilint.py current.txt previous.txt
238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey"""
248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
25294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeyimport re, sys, collections
268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
288190f4885b3eb34231877003a583116a0e82826eJeff SharkeyBLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
308190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    codes = []
338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if reset: codes.append("0")
348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    else:
358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not fg is None: codes.append("3%d" % (fg))
368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not bg is None:
378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not bright: codes.append("4%d" % (bg))
388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            else: codes.append("10%d" % (bg))
398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if bold: codes.append("1")
408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        elif dim: codes.append("2")
418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else: codes.append("22")
428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return "\033[%sm" % (";".join(codes))
438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
458190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Field():
468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __init__(self, clazz, raw):
478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.clazz = clazz
488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.typ = raw[0]
578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[1].strip(";")
588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(raw) >= 4 and raw[2] == "=":
598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.value = raw[3].strip(';"')
608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else:
618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.value = None
628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
678190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Method():
688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __init__(self, clazz, raw):
698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.clazz = clazz
708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = re.split("[\s(),;]+", raw)
738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["", ";"]:
748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract"]:
788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.typ = raw[0]
818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[1]
828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.args = []
838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in raw[2:]:
848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if r == "throws": break
858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.args.append(r)
868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
918190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Class():
928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __init__(self, pkg, raw):
938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.pkg = pkg
948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.ctors = []
968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.fields = []
978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.methods = []
988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
1008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
1018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "class" in raw:
1028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.fullname = raw[raw.index("class")+1]
1038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        elif "interface" in raw:
1048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.fullname = raw[raw.index("interface")+1]
1058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "." in self.fullname:
1078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.name = self.fullname[self.fullname.rindex(".")+1:]
1088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else:
1098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.name = self.fullname
1108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1158190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Package():
1168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __init__(self, raw):
1178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
1188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
1208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[raw.index("package")+1]
1218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1268190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef parse_api(fn):
1278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    api = []
1288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    pkg = None
1298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    clazz = None
1308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    with open(fn) as f:
1328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for raw in f.readlines():
1338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            raw = raw.rstrip()
1348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if raw.startswith("package"):
1368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                pkg = Package(raw)
1378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            elif raw.startswith("  ") and raw.endswith("{"):
1388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                clazz = Class(pkg, raw)
1398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                api.append(clazz)
1408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            elif raw.startswith("    ctor"):
1418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                clazz.ctors.append(Method(clazz, raw))
1428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            elif raw.startswith("    method"):
1438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                clazz.methods.append(Method(clazz, raw))
1448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            elif raw.startswith("    field"):
1458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                clazz.fields.append(Field(clazz, raw))
1468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return api
1488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1508190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyfailures = []
1518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
152294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef filter_dupe(s):
153294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    return s.replace(" deprecated ", " ")
154294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
1558190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef _fail(clazz, detail, msg):
1568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Records an API failure to be processed later."""
1578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    global failures
1588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    res = msg
1608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if detail is not None:
1618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        res += "\n    in " + repr(detail)
1628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    res += "\n    in " + repr(clazz)
1638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    res += "\n    in " + repr(clazz.pkg)
164294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    failures.append(filter_dupe(res))
1658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1668190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef warn(clazz, detail, msg):
1678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK), format(reset=True), msg))
1688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1698190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef error(clazz, detail, msg):
1708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK), format(reset=True), msg))
1718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1738190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_constants(clazz):
1748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """All static final constants must be FOO_NAME style."""
1758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.match("R\.[a-z]+", clazz.fullname): return
1768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
1788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split:
1798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if re.match("[A-Z0-9_]+", f.name) is None:
1808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                error(clazz, f, "Constant field names should be FOO_NAME")
1818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1838190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_enums(clazz):
1848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Enums are bad, mmkay?"""
1858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends java.lang.Enum" in clazz.raw:
1868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        error(clazz, None, "Enums are not allowed")
1878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1898190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_class_names(clazz):
1908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Try catching malformed class names like myMtp or MTPUser."""
1918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.search("[A-Z]{2,}", clazz.name) is not None:
1928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        warn(clazz, None, "Class name style should be Mtp not MTP")
1938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.match("[^A-Z]", clazz.name):
1948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        error(clazz, None, "Class must start with uppercase char")
1958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1978190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_method_names(clazz):
1988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Try catching malformed method names, like Foo() or getMTU()."""
1998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.pkg.name == "android.opengl": return
2008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
2028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.search("[A-Z]{2,}", m.name) is not None:
2038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            warn(clazz, m, "Method name style should be getMtu() instead of getMTU()")
2048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.match("[^a-z]", m.name):
2058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, None, "Method name must start with lowercase char")
2068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2088190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_callbacks(clazz):
2098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify Callback classes.
2108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All callback classes must be abstract.
2118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All methods must follow onFoo() naming style."""
2128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Callbacks"):
2148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        error(clazz, None, "Class must be named exactly Callback")
2158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Observer"):
2168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        warn(clazz, None, "Class should be named Callback")
2178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Callback"):
2198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "interface" in clazz.split:
2208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, None, "Callback must be abstract class")
2218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for m in clazz.methods:
2238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("on[A-Z][a-z]*", m.name):
2248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                error(clazz, m, "Callback method names must be onFoo style")
2258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2278190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_listeners(clazz):
2288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify Listener classes.
2298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All Listener classes must be interface.
2308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All methods must follow onFoo() naming style.
2318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    If only a single method, it must match class name:
2328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        interface OnFooListener { void onFoo() }"""
2338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Listener"):
2358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if " abstract class " in clazz.raw:
2368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, None, "Listener should be interface")
2378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for m in clazz.methods:
2398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("on[A-Z][a-z]*", m.name):
2408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                error(clazz, m, "Listener method names must be onFoo style")
2418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(clazz.methods) == 1 and clazz.name.startswith("On"):
2438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            m = clazz.methods[0]
2448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if (m.name + "Listener").lower() != clazz.name.lower():
2458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                error(clazz, m, "Single method name should match class name")
2468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2488190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_actions(clazz):
2498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify intent actions.
2508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All action names must be named ACTION_FOO.
2518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All action values must be scoped by package and match name:
2528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        package android.foo {
2538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            String ACTION_BAR = "android.foo.action.BAR";
2548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        }"""
2558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
2568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.value is None: continue
2578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.name.startswith("EXTRA_"): continue
2588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
2608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower():
2618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if not f.name.startswith("ACTION_"):
2628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    error(clazz, f, "Intent action must be ACTION_FOO")
2638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                else:
2648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if clazz.name == "Intent":
2658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.intent.action"
2668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    elif clazz.name == "Settings":
2678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.settings"
2688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    else:
2698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = clazz.pkg.name + ".action"
2708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    expected = prefix + "." + f.name[7:]
2718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if f.value != expected:
2728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        error(clazz, f, "Inconsistent action value")
2738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2758190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_extras(clazz):
2768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify intent extras.
2778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All extra names must be named EXTRA_FOO.
2788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All extra values must be scoped by package and match name:
2798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        package android.foo {
2808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            String EXTRA_BAR = "android.foo.extra.BAR";
2818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        }"""
2828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
2838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.value is None: continue
2848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.name.startswith("ACTION_"): continue
2858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
2878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower():
2888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if not f.name.startswith("EXTRA_"):
2898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    error(clazz, f, "Intent extra must be EXTRA_FOO")
2908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                else:
2918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if clazz.name == "Intent":
2928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.intent.extra"
2938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    else:
2948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = clazz.pkg.name + ".extra"
2958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    expected = prefix + "." + f.name[6:]
2968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if f.value != expected:
2978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        error(clazz, f, "Inconsistent extra value")
2988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3008190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_equals(clazz):
3018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that equals() and hashCode() must be overridden together."""
3028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    methods = [ m.name for m in clazz.methods ]
3038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    eq = "equals" in methods
3048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    hc = "hashCode" in methods
3058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if eq != hc:
3068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        error(clazz, None, "Must override both equals and hashCode")
3078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3098190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_parcelable(clazz):
3108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that Parcelable objects aren't hiding required bits."""
3118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "implements android.os.Parcelable" in clazz.raw:
3128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
3138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
3148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        describe = [ i for i in clazz.methods if i.name == "describeContents" ]
3158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
3178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents")
3188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3208190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_protected(clazz):
3218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that no protected methods are allowed."""
3228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
3238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "protected" in m.split:
3248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, m, "Protected method")
3258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "protected" in f.split:
3278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, f, "Protected field")
3288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3308190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_fields(clazz):
3318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that all exposed fields are final.
3328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Exposed fields must follow myName style.
3338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Catch internal mFoo objects being exposed."""
3348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not "final" in f.split:
3368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, f, "Bare fields must be final; consider adding accessors")
3378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not "static" in f.split:
3398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("[a-z]([a-zA-Z]+)?", f.name):
3408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                error(clazz, f, "Non-static fields must be myName")
3418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.match("[m][A-Z]", f.name):
3438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, f, "Don't expose your internal objects")
3448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3468190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_register(clazz):
3478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify parity of registration methods.
3488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Callback objects use register/unregister methods.
3498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Listener objects use add/remove methods."""
3508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    methods = [ m.name for m in clazz.methods ]
3518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
3528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "Callback" in m.raw:
3538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("register"):
3548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "unregister" + m.name[8:]
3558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
3568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    error(clazz, m, "Missing unregister")
3578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("unregister"):
3588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "register" + m.name[10:]
3598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
3608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    error(clazz, m, "Missing register")
3618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("add") or m.name.startswith("remove"):
3638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                error(clazz, m, "Callback should be register/unregister")
3648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "Listener" in m.raw:
3668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("add"):
3678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "remove" + m.name[3:]
3688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
3698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    error(clazz, m, "Missing remove")
3708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("remove") and not m.name.startswith("removeAll"):
3718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "add" + m.name[6:]
3728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
3738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    error(clazz, m, "Missing add")
3748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("register") or m.name.startswith("unregister"):
3768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                error(clazz, m, "Listener should be add/remove")
3778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3798190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_sync(clazz):
3808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify synchronized methods aren't exposed."""
3818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
3828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "synchronized" in m.split:
3838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, m, "Lock exposed")
3848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3868190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_intent_builder(clazz):
3878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that Intent builders are createFooIntent() style."""
3888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name == "Intent": return
3898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
3918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.typ == "android.content.Intent":
3928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("create") and m.name.endswith("Intent"):
3938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                pass
3948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            else:
3958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                warn(clazz, m, "Should be createFooIntent()")
3968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3988190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_helper_classes(clazz):
399294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verify that helper classes are named consistently with what they extend.
400294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    All developer extendable methods should be named onFoo()."""
401294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    test_methods = False
4028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.app.Service" in clazz.raw:
403294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
4048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Service"):
4058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, None, "Inconsistent class name")
4068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.content.ContentProvider" in clazz.raw:
407294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
4088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Provider"):
4098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, None, "Inconsistent class name")
4108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.content.BroadcastReceiver" in clazz.raw:
411294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
4128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Receiver"):
4138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, None, "Inconsistent class name")
414294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    if "extends android.app.Activity" in clazz.raw:
415294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
416294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if not clazz.name.endswith("Activity"):
417294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            error(clazz, None, "Inconsistent class name")
418294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
419294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    if test_methods:
420294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        for m in clazz.methods:
421294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if "final" in m.split: continue
422294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if not re.match("on[A-Z]", m.name):
423294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                error(clazz, m, "Extendable methods should be onFoo() style, otherwise final")
4248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4268190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_builder(clazz):
4278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify builder classes.
4288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Methods should return the builder to enable chaining."""
4298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if " extends " in clazz.raw: return
4308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if not clazz.name.endswith("Builder"): return
4318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name != "Builder":
4338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        warn(clazz, None, "Should be standalone Builder class")
4348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    has_build = False
4368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
4378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name == "build":
4388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            has_build = True
4398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            continue
4408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name.startswith("get"): continue
4428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name.startswith("clear"): continue
4438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
444294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.name.startswith("with"):
445294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            error(clazz, m, "Builder methods must be setFoo()")
446294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
447294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.name.startswith("set"):
448294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if not m.typ.endswith(clazz.fullname):
449294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                warn(clazz, m, "Should return the builder")
4508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if not has_build:
4528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        warn(clazz, None, "Missing build() method")
4538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4558190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_aidl(clazz):
4568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Catch people exposing raw AIDL."""
457932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
4588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        error(clazz, None, "Exposing raw AIDL interface")
4598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
461932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_internal(clazz):
462932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    """Catch people exposing internal classes."""
463932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if clazz.pkg.name.startswith("com.android"):
464932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        error(clazz, None, "Exposing internal class")
465932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
466932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
467932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_layering(clazz):
468932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    """Catch package layering violations.
469932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    For example, something in android.os depending on android.app."""
470932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    ranking = [
471932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
472932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.app",
473932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.widget",
474932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.view",
475932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.animation",
476932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.provider",
477932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.content",
478932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.database",
479932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.graphics",
480932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.text",
481932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.os",
482932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.util"
483932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    ]
484932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
485932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    def rank(p):
486932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        for i in range(len(ranking)):
487932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            if isinstance(ranking[i], list):
488932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                for j in ranking[i]:
489932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                    if p.startswith(j): return i
490932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            else:
491932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                if p.startswith(ranking[i]): return i
492932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
493932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    cr = rank(clazz.pkg.name)
494932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if cr is None: return
495932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
496932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    for f in clazz.fields:
497932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ir = rank(f.typ)
498932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        if ir and ir < cr:
499932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            warn(clazz, f, "Field type violates package layering")
500932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
501932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    for m in clazz.methods:
502932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ir = rank(m.typ)
503932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        if ir and ir < cr:
504932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            warn(clazz, m, "Method return type violates package layering")
505932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        for arg in m.args:
506932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            ir = rank(arg)
507932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            if ir and ir < cr:
508932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                warn(clazz, m, "Method argument type violates package layering")
509932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
510932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
511294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_boolean(clazz):
512294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Catches people returning boolean from getFoo() style methods.
513294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    Ignores when matching setFoo() is present."""
514294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    methods = [ m.name for m in clazz.methods ]
515294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for m in clazz.methods:
516294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.typ == "boolean" and m.name.startswith("get") and m.name != "get" and len(m.args) == 0:
517294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            setter = "set" + m.name[3:]
518294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if setter not in methods:
519294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                error(clazz, m, "Methods returning boolean should be isFoo or hasFoo")
520294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
521294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
522294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_collections(clazz):
523294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verifies that collection types are interfaces."""
524294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    bad = ["java.util.Vector", "java.util.LinkedList", "java.util.ArrayList", "java.util.Stack",
525294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey           "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"]
526294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for m in clazz.methods:
527294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        filt = re.sub("<.+>", "", m.typ)
528294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if filt in bad:
529294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            error(clazz, m, "Return type is concrete collection")
530294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        for arg in m.args:
531294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            filt = re.sub("<.+>", "", arg)
532294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if filt in bad:
533294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                error(clazz, m, "Argument is concrete collection")
534294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
535294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
536294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_flags(clazz):
537294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verifies that flags are non-overlapping."""
538294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    known = collections.defaultdict(int)
539294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for f in clazz.fields:
540294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if "FLAG_" in f.name:
541294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            try:
542294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                val = int(f.value)
543294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            except:
544294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                continue
545294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
546294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            scope = f.name[0:f.name.index("FLAG_")]
547294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if val & known[scope]:
548294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                warn(clazz, f, "Found overlapping flag")
549294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            known[scope] |= val
550294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
551294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
5528190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_all(api):
5538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    global failures
5548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    failures = []
5568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for clazz in api:
5578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("java"): continue
5588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("junit"): continue
5598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("org.apache"): continue
5608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("org.xml"): continue
5618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("org.json"): continue
5628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("org.w3c"): continue
5638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_constants(clazz)
5658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_enums(clazz)
5668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_class_names(clazz)
5678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_method_names(clazz)
5688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_callbacks(clazz)
5698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_listeners(clazz)
5708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_actions(clazz)
5718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_extras(clazz)
5728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_equals(clazz)
5738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_parcelable(clazz)
5748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_protected(clazz)
5758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_fields(clazz)
5768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_register(clazz)
5778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_sync(clazz)
5788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_intent_builder(clazz)
5798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_helper_classes(clazz)
5808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_builder(clazz)
5818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_aidl(clazz)
582932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        verify_internal(clazz)
583932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        verify_layering(clazz)
584294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        verify_boolean(clazz)
585294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        verify_collections(clazz)
586294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        verify_flags(clazz)
5878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return failures
5898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5918190f4885b3eb34231877003a583116a0e82826eJeff Sharkeycur = parse_api(sys.argv[1])
5928190f4885b3eb34231877003a583116a0e82826eJeff Sharkeycur_fail = verify_all(cur)
5938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5948190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyif len(sys.argv) > 2:
5958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    prev = parse_api(sys.argv[2])
5968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    prev_fail = verify_all(prev)
5978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    # ignore errors from previous API level
5998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for p in prev_fail:
6008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if p in cur_fail:
6018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            cur_fail.remove(p)
6028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6048190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyfor f in cur_fail:
6058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    print f
6068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    print
607