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
231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
241498f9c615395de17e11204b962d7d925e5f222dJeff SharkeyYou can also splice in blame details like this:
251498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey$ git blame api/current.txt -t -e > /tmp/currentblame.txt
261498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey$ apilint.py /tmp/currentblame.txt previous.txt --no-color
278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey"""
288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
291498f9c615395de17e11204b962d7d925e5f222dJeff Sharkeyimport re, sys, collections, traceback
308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
328190f4885b3eb34231877003a583116a0e82826eJeff SharkeyBLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
348190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
361498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if "--no-color" in sys.argv: return ""
378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    codes = []
388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if reset: codes.append("0")
398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    else:
408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not fg is None: codes.append("3%d" % (fg))
418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not bg is None:
428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not bright: codes.append("4%d" % (bg))
438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            else: codes.append("10%d" % (bg))
448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if bold: codes.append("1")
458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        elif dim: codes.append("2")
468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else: codes.append("22")
478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return "\033[%sm" % (";".join(codes))
488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
508190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Field():
511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    def __init__(self, clazz, raw, blame):
528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.clazz = clazz
538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.typ = raw[0]
638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[1].strip(";")
648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(raw) >= 4 and raw[2] == "=":
658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.value = raw[3].strip(';"')
668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        else:
678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.value = None
688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
69037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        self.ident = self.raw.replace(" deprecated ", " ")
70037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
758190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Method():
761498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    def __init__(self, clazz, raw, blame):
778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.clazz = clazz
788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
791498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
801498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
811498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        # drop generics for now
821498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        raw = re.sub("<.+?>", "", raw)
838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = re.split("[\s(),;]+", raw)
858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["", ";"]:
868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract"]:
908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            while r in raw: raw.remove(r)
918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.typ = raw[0]
938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[1]
948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.args = []
958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for r in raw[2:]:
968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if r == "throws": break
978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.args.append(r)
988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
99037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        # identity for compat purposes
100037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = self.raw
101037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = ident.replace(" deprecated ", " ")
102037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = ident.replace(" synchronized ", " ")
103037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        ident = re.sub("<.+?>", "", ident)
104037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if " throws " in ident:
105037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            ident = ident[:ident.index(" throws ")]
106037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        self.ident = ident
107037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
1088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1128190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Class():
1131498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    def __init__(self, pkg, raw, blame):
1148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.pkg = pkg
1158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
1161498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
1178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.ctors = []
1188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.fields = []
1198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.methods = []
1208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
1228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.split = list(raw)
1238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "class" in raw:
1248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.fullname = raw[raw.index("class")+1]
1258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        elif "interface" in raw:
1268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            self.fullname = raw[raw.index("interface")+1]
127037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        else:
128037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            raise ValueError("Funky class type %s" % (self.raw))
129037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
130037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if "extends" in raw:
131037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            self.extends = raw[raw.index("extends")+1]
132037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        else:
133037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            self.extends = None
1348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1351498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.fullname = self.pkg.name + "." + self.fullname
1361498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.name = self.fullname[self.fullname.rindex(".")+1:]
1371498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
1388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1428190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Package():
1431498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    def __init__(self, raw, blame):
1448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.raw = raw.strip(" {;")
1451498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        self.blame = blame
1468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        raw = raw.split()
1488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        self.name = raw[raw.index("package")+1]
1498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    def __repr__(self):
1518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        return self.raw
1528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1548190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef parse_api(fn):
1551498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    api = {}
1568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    pkg = None
1578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    clazz = None
1581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    blame = None
1591498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
1601498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
1618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    with open(fn) as f:
1638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for raw in f.readlines():
1648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            raw = raw.rstrip()
1651498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            match = re_blame.match(raw)
1661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if match is not None:
1671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                blame = match.groups()[0:2]
1681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                raw = match.groups()[2]
1691498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            else:
1701498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                blame = None
1718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if raw.startswith("package"):
1731498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pkg = Package(raw, blame)
1748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            elif raw.startswith("  ") and raw.endswith("{"):
1751498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                clazz = Class(pkg, raw, blame)
1761498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                api[clazz.fullname] = clazz
1778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            elif raw.startswith("    ctor"):
1781498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                clazz.ctors.append(Method(clazz, raw, blame))
1798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            elif raw.startswith("    method"):
1801498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                clazz.methods.append(Method(clazz, raw, blame))
1818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            elif raw.startswith("    field"):
1821498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                clazz.fields.append(Field(clazz, raw, blame))
1838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return api
1858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkeyfailures = {}
188294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
1898190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef _fail(clazz, detail, msg):
1908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Records an API failure to be processed later."""
1918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    global failures
1928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
1931498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
1941498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    sig = sig.replace(" deprecated ", " ")
1951498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
1968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    res = msg
1971498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    blame = clazz.blame
1988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if detail is not None:
1998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        res += "\n    in " + repr(detail)
2001498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        blame = detail.blame
2018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    res += "\n    in " + repr(clazz)
2028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    res += "\n    in " + repr(clazz.pkg)
2031498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if blame is not None:
2041498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        res += "\n    last modified by %s in %s" % (blame[1], blame[0])
2051498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    failures[sig] = res
2068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2078190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef warn(clazz, detail, msg):
208037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), format(reset=True), msg))
2098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2108190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef error(clazz, detail, msg):
211037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK, bold=True), format(reset=True), msg))
2128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2148190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_constants(clazz):
2158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """All static final constants must be FOO_NAME style."""
2161498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if re.match("android\.R\.[a-z]+", clazz.fullname): return
2178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
2198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split:
2208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if re.match("[A-Z0-9_]+", f.name) is None:
2218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                error(clazz, f, "Constant field names should be FOO_NAME")
2228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2248190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_enums(clazz):
2258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Enums are bad, mmkay?"""
2268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends java.lang.Enum" in clazz.raw:
2278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        error(clazz, None, "Enums are not allowed")
2288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2308190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_class_names(clazz):
2318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Try catching malformed class names like myMtp or MTPUser."""
2321498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
2331498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.renderscript"): return
2341498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if re.match("android\.R\.[a-z]+", clazz.fullname): return
2351498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
2368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.search("[A-Z]{2,}", clazz.name) is not None:
2378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        warn(clazz, None, "Class name style should be Mtp not MTP")
2388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if re.match("[^A-Z]", clazz.name):
2398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        error(clazz, None, "Class must start with uppercase char")
2408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2428190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_method_names(clazz):
2438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Try catching malformed method names, like Foo() or getMTU()."""
2441498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.opengl"): return
2451498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname.startswith("android.renderscript"): return
2461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.system.OsConstants": return
2478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
2498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.search("[A-Z]{2,}", m.name) is not None:
2508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            warn(clazz, m, "Method name style should be getMtu() instead of getMTU()")
2518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if re.match("[^a-z]", m.name):
2521498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, m, "Method name must start with lowercase char")
2538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2558190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_callbacks(clazz):
2568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify Callback classes.
2578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All callback classes must be abstract.
2588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All methods must follow onFoo() naming style."""
2591498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.speech.tts.SynthesisCallback": return
2608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Callbacks"):
2621498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        error(clazz, None, "Class name must not be plural")
2638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Observer"):
2641498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        warn(clazz, None, "Class should be named FooCallback")
2658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Callback"):
2678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "interface" in clazz.split:
2681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, None, "Callback must be abstract class to enable extension in future API levels")
2698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for m in clazz.methods:
2718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("on[A-Z][a-z]*", m.name):
2721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, m, "Callback method names must be onFoo() style")
2738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2758190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_listeners(clazz):
2768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify Listener classes.
2778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All Listener classes must be interface.
2788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All methods must follow onFoo() naming style.
2798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    If only a single method, it must match class name:
2808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        interface OnFooListener { void onFoo() }"""
2818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name.endswith("Listener"):
2838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if " abstract class " in clazz.raw:
2841498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, None, "Listener should be an interface, otherwise renamed Callback")
2858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        for m in clazz.methods:
2878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("on[A-Z][a-z]*", m.name):
2881498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, m, "Listener method names must be onFoo() style")
2898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(clazz.methods) == 1 and clazz.name.startswith("On"):
2918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            m = clazz.methods[0]
2928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if (m.name + "Listener").lower() != clazz.name.lower():
2931498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, m, "Single listener method name should match class name")
2948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
2968190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_actions(clazz):
2978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify intent actions.
2988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All action names must be named ACTION_FOO.
2998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All action values must be scoped by package and match name:
3008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        package android.foo {
3018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            String ACTION_BAR = "android.foo.action.BAR";
3028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        }"""
3038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.value is None: continue
3058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.name.startswith("EXTRA_"): continue
3061498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if f.name == "SERVICE_INTERFACE" or f.name == "PROVIDER_INTERFACE": continue
3078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
3098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower():
3108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if not f.name.startswith("ACTION_"):
3111498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    error(clazz, f, "Intent action constant name must be ACTION_FOO")
3128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                else:
3131498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    if clazz.fullname == "android.content.Intent":
3148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.intent.action"
3151498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.fullname == "android.provider.Settings":
3168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.settings"
3171498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.fullname == "android.app.admin.DevicePolicyManager" or clazz.fullname == "android.app.admin.DeviceAdminReceiver":
3181498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                        prefix = "android.app.action"
3198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    else:
3208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = clazz.pkg.name + ".action"
3218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    expected = prefix + "." + f.name[7:]
3228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if f.value != expected:
3231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                        error(clazz, f, "Inconsistent action value; expected %s" % (expected))
3248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3268190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_extras(clazz):
3278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify intent extras.
3288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All extra names must be named EXTRA_FOO.
3298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    All extra values must be scoped by package and match name:
3308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        package android.foo {
3318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            String EXTRA_BAR = "android.foo.extra.BAR";
3328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        }"""
3331498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.app.Notification": return
3341498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.appwidget.AppWidgetManager": return
3351498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
3368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.value is None: continue
3388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if f.name.startswith("ACTION_"): continue
3398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
3418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower():
3428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if not f.name.startswith("EXTRA_"):
3438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    error(clazz, f, "Intent extra must be EXTRA_FOO")
3448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                else:
3451498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    if clazz.pkg.name == "android.content" and clazz.name == "Intent":
3468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = "android.intent.extra"
3471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    elif clazz.pkg.name == "android.app.admin":
3481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                        prefix = "android.app.extra"
3498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    else:
3508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                        prefix = clazz.pkg.name + ".extra"
3518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    expected = prefix + "." + f.name[6:]
3528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                    if f.value != expected:
3531498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                        error(clazz, f, "Inconsistent extra value; expected %s" % (expected))
3548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3568190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_equals(clazz):
3578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that equals() and hashCode() must be overridden together."""
3588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    methods = [ m.name for m in clazz.methods ]
3598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    eq = "equals" in methods
3608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    hc = "hashCode" in methods
3618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if eq != hc:
3621498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        error(clazz, None, "Must override both equals and hashCode; missing one")
3638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3658190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_parcelable(clazz):
3668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that Parcelable objects aren't hiding required bits."""
3678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "implements android.os.Parcelable" in clazz.raw:
3688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
3698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
3708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        describe = [ i for i in clazz.methods if i.name == "describeContents" ]
3718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
3731498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
3748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3768190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_protected(clazz):
3778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that no protected methods are allowed."""
3788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
3798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "protected" in m.split:
3801498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, m, "No protected methods; must be public")
3818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
3828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "protected" in f.split:
3831498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, f, "No protected fields; must be public")
3848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
3868190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_fields(clazz):
3878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that all exposed fields are final.
3888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Exposed fields must follow myName style.
3898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Catch internal mFoo objects being exposed."""
3901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
3911498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    IGNORE_BARE_FIELDS = [
3921498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.app.ActivityManager.RecentTaskInfo",
3931498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.app.Notification",
3941498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.ActivityInfo",
3951498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.ApplicationInfo",
3961498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.FeatureGroupInfo",
3971498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.InstrumentationInfo",
3981498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.PackageInfo",
3991498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.content.pm.PackageItemInfo",
4001498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.os.Message",
4011498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        "android.system.StructPollfd",
4021498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    ]
4031498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for f in clazz.fields:
4058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not "final" in f.split:
4061498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if clazz.fullname in IGNORE_BARE_FIELDS:
4071498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4081498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            elif clazz.fullname.endswith("LayoutParams"):
4091498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4101498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            elif clazz.fullname.startswith("android.util.Mutable"):
4111498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
4121498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            else:
4131498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, f, "Bare fields must be marked final; consider adding accessors")
4148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not "static" in f.split:
4168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if not re.match("[a-z]([a-zA-Z]+)?", f.name):
4171498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, f, "Non-static fields must be named with myField style")
4188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4191498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if re.match("[ms][A-Z]", f.name):
4208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            error(clazz, f, "Don't expose your internal objects")
4218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4221498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if re.match("[A-Z_]+", f.name):
4231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if "static" not in f.split or "final" not in f.split:
4241498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, f, "Constants must be marked static final")
4251498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4278190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_register(clazz):
4288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify parity of registration methods.
4298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Callback objects use register/unregister methods.
4308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Listener objects use add/remove methods."""
4318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    methods = [ m.name for m in clazz.methods ]
4328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
4338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "Callback" in m.raw:
4348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("register"):
4358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "unregister" + m.name[8:]
4368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
4371498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    error(clazz, m, "Missing unregister method")
4388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("unregister"):
4398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "register" + m.name[10:]
4408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
4411498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    error(clazz, m, "Missing register method")
4428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("add") or m.name.startswith("remove"):
4441498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, m, "Callback methods should be named register/unregister")
4458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "Listener" in m.raw:
4478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("add"):
4488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "remove" + m.name[3:]
4498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
4501498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    error(clazz, m, "Missing remove method")
4518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("remove") and not m.name.startswith("removeAll"):
4528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                other = "add" + m.name[6:]
4538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                if other not in methods:
4541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    error(clazz, m, "Missing add method")
4558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("register") or m.name.startswith("unregister"):
4571498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, m, "Listener methods should be named add/remove")
4588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4608190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_sync(clazz):
4618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify synchronized methods aren't exposed."""
4628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
4638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if "synchronized" in m.split:
4641498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, m, "Internal lock exposed")
4658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4678190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_intent_builder(clazz):
4688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify that Intent builders are createFooIntent() style."""
4698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name == "Intent": return
4708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
4728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.typ == "android.content.Intent":
4738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            if m.name.startswith("create") and m.name.endswith("Intent"):
4748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey                pass
4758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            else:
4761498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, m, "Methods creating an Intent should be named createFooIntent()")
4778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
4798190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_helper_classes(clazz):
480294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verify that helper classes are named consistently with what they extend.
481294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    All developer extendable methods should be named onFoo()."""
482294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    test_methods = False
4838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.app.Service" in clazz.raw:
484294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
4858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Service"):
4861498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, None, "Inconsistent class name; should be FooService")
4871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4881498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        found = False
4891498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        for f in clazz.fields:
4901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if f.name == "SERVICE_INTERFACE":
4911498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                found = True
4921498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if f.value != clazz.fullname:
4931498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    error(clazz, f, "Inconsistent interface constant; expected %s" % (clazz.fullname))
4941498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4951498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if not found:
4961498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            warn(clazz, None, "Missing SERVICE_INTERFACE constant")
4971498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
4981498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if "abstract" in clazz.split and not clazz.fullname.startswith("android.service."):
4991498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            warn(clazz, None, "Services extended by developers should be under android.service")
5001498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.content.ContentProvider" in clazz.raw:
502294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
5038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Provider"):
5041498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, None, "Inconsistent class name; should be FooProvider")
5051498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5061498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        found = False
5071498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        for f in clazz.fields:
5081498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if f.name == "PROVIDER_INTERFACE":
5091498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                found = True
5101498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if f.value != clazz.fullname:
5111498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    error(clazz, f, "Inconsistent interface name; expected %s" % (clazz.fullname))
5121498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5131498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if not found:
5141498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            warn(clazz, None, "Missing PROVIDER_INTERFACE constant")
5151498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5161498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if "abstract" in clazz.split and not clazz.fullname.startswith("android.provider."):
5171498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            warn(clazz, None, "Providers extended by developers should be under android.provider")
5181498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
5198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if "extends android.content.BroadcastReceiver" in clazz.raw:
520294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
5218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if not clazz.name.endswith("Receiver"):
5221498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, None, "Inconsistent class name; should be FooReceiver")
5231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
524294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    if "extends android.app.Activity" in clazz.raw:
525294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        test_methods = True
526294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if not clazz.name.endswith("Activity"):
5271498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, None, "Inconsistent class name; should be FooActivity")
528294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
529294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    if test_methods:
530294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        for m in clazz.methods:
531294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if "final" in m.split: continue
532294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if not re.match("on[A-Z]", m.name):
5331498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                if "abstract" in m.split:
5341498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    error(clazz, m, "Methods implemented by developers must be named onFoo()")
5351498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                else:
5361498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                    warn(clazz, m, "If implemented by developer, should be named onFoo(); otherwise consider marking final")
5378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5398190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_builder(clazz):
5408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Verify builder classes.
5418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    Methods should return the builder to enable chaining."""
5428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if " extends " in clazz.raw: return
5438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if not clazz.name.endswith("Builder"): return
5448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if clazz.name != "Builder":
5461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        warn(clazz, None, "Builder should be defined as inner class")
5478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    has_build = False
5498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for m in clazz.methods:
5508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name == "build":
5518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            has_build = True
5528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey            continue
5538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name.startswith("get"): continue
5558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if m.name.startswith("clear"): continue
5568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
557294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.name.startswith("with"):
5581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, m, "Builder methods names must follow setFoo() style")
559294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
560294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.name.startswith("set"):
561294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if not m.typ.endswith(clazz.fullname):
5621498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                warn(clazz, m, "Methods should return the builder")
5638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    if not has_build:
5658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        warn(clazz, None, "Missing build() method")
5668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5688190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_aidl(clazz):
5698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    """Catch people exposing raw AIDL."""
570932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
5718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        error(clazz, None, "Exposing raw AIDL interface")
5728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
5738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
574932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_internal(clazz):
575932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    """Catch people exposing internal classes."""
576932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if clazz.pkg.name.startswith("com.android"):
577932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        error(clazz, None, "Exposing internal class")
578932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
579932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
580932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_layering(clazz):
581932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    """Catch package layering violations.
582932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    For example, something in android.os depending on android.app."""
583932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    ranking = [
584932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
585932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.app",
586932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.widget",
587932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.view",
588932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.animation",
589932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.provider",
5901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        ["android.content","android.graphics.drawable"],
591932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.database",
592932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.graphics",
593932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.text",
594932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.os",
595932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        "android.util"
596932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    ]
597932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
598932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    def rank(p):
599932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        for i in range(len(ranking)):
600932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            if isinstance(ranking[i], list):
601932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                for j in ranking[i]:
602932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                    if p.startswith(j): return i
603932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            else:
604932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                if p.startswith(ranking[i]): return i
605932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
606932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    cr = rank(clazz.pkg.name)
607932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    if cr is None: return
608932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
609932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    for f in clazz.fields:
610932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ir = rank(f.typ)
611932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        if ir and ir < cr:
612932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            warn(clazz, f, "Field type violates package layering")
613932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
614932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey    for m in clazz.methods:
615932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        ir = rank(m.typ)
616932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        if ir and ir < cr:
617932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            warn(clazz, m, "Method return type violates package layering")
618932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        for arg in m.args:
619932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            ir = rank(arg)
620932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey            if ir and ir < cr:
621932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey                warn(clazz, m, "Method argument type violates package layering")
622932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
623932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey
6241498f9c615395de17e11204b962d7d925e5f222dJeff Sharkeydef verify_boolean(clazz, api):
625294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Catches people returning boolean from getFoo() style methods.
626294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    Ignores when matching setFoo() is present."""
6271498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
628294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    methods = [ m.name for m in clazz.methods ]
6291498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
6301498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    builder = clazz.fullname + ".Builder"
6311498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    builder_methods = []
6321498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if builder in api:
6331498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        builder_methods = [ m.name for m in api[builder].methods ]
6341498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
635294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for m in clazz.methods:
636294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if m.typ == "boolean" and m.name.startswith("get") and m.name != "get" and len(m.args) == 0:
637294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            setter = "set" + m.name[3:]
6381498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if setter in methods:
6391498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
6401498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            elif builder is not None and setter in builder_methods:
6411498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                pass
6421498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            else:
6431498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                warn(clazz, m, "Methods returning boolean should be named isFoo, hasFoo, areFoo")
644294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
645294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
646294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_collections(clazz):
647294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verifies that collection types are interfaces."""
6481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    if clazz.fullname == "android.os.Bundle": return
6491498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
650294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    bad = ["java.util.Vector", "java.util.LinkedList", "java.util.ArrayList", "java.util.Stack",
651294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey           "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"]
652294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for m in clazz.methods:
6531498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        if m.typ in bad:
6541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            error(clazz, m, "Return type is concrete collection; should be interface")
655294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        for arg in m.args:
6561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            if arg in bad:
6571498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                error(clazz, m, "Argument is concrete collection; should be interface")
658294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
659294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
660294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_flags(clazz):
661294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    """Verifies that flags are non-overlapping."""
662294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    known = collections.defaultdict(int)
663294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey    for f in clazz.fields:
664294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        if "FLAG_" in f.name:
665294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            try:
666294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                val = int(f.value)
667294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            except:
668294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey                continue
669294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
670294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            scope = f.name[0:f.name.index("FLAG_")]
671294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            if val & known[scope]:
6721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey                warn(clazz, f, "Found overlapping flag constant value")
673294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey            known[scope] |= val
674294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
675294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey
676037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkeydef verify_style(api):
677037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    """Find all style issues in the given API level."""
6788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    global failures
6798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6801498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    failures = {}
6811498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    for key in sorted(api.keys()):
6821498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        clazz = api[key]
6831498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey
6848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("java"): continue
6858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("junit"): continue
6868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("org.apache"): continue
6878190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("org.xml"): continue
6888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("org.json"): continue
6898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if clazz.pkg.name.startswith("org.w3c"): continue
6908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
6918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_constants(clazz)
6928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_enums(clazz)
6938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_class_names(clazz)
6948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_method_names(clazz)
6958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_callbacks(clazz)
6968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_listeners(clazz)
6978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_actions(clazz)
6988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_extras(clazz)
6998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_equals(clazz)
7008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_parcelable(clazz)
7018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_protected(clazz)
7028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_fields(clazz)
7038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_register(clazz)
7048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_sync(clazz)
7058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_intent_builder(clazz)
7068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_helper_classes(clazz)
7078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_builder(clazz)
7088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        verify_aidl(clazz)
709932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        verify_internal(clazz)
710932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey        verify_layering(clazz)
7111498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey        verify_boolean(clazz, api)
712294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        verify_collections(clazz)
713294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey        verify_flags(clazz)
7148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
7158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    return failures
7168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
7178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
718037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkeydef verify_compat(cur, prev):
719037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    """Find any incompatible API changes between two levels."""
720037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    global failures
721037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
722037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def class_exists(api, test):
723037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return test.fullname in api
724037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
725037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def ctor_exists(api, clazz, test):
726037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for m in clazz.ctors:
727037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if m.ident == test.ident: return True
728037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
729037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
730037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def all_methods(api, clazz):
731037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = list(clazz.methods)
732037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if clazz.extends is not None:
733037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            methods.extend(all_methods(api, api[clazz.extends]))
734037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return methods
735037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
736037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def method_exists(api, clazz, test):
737037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = all_methods(api, clazz)
738037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for m in methods:
739037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if m.ident == test.ident: return True
740037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
741037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
742037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    def field_exists(api, clazz, test):
743037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for f in clazz.fields:
744037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if f.ident == test.ident: return True
745037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        return False
746037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
747037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    failures = {}
748037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    for key in sorted(prev.keys()):
749037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        prev_clazz = prev[key]
750037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
751037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        if not class_exists(cur, prev_clazz):
752037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            error(prev_clazz, None, "Class removed or incompatible change")
753037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            continue
754037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
755037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        cur_clazz = cur[key]
756037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
757037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in prev_clazz.ctors:
758037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not ctor_exists(cur, cur_clazz, test):
759037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey                error(prev_clazz, prev_ctor, "Constructor removed or incompatible change")
760037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
761037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        methods = all_methods(prev, prev_clazz)
762037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in methods:
763037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not method_exists(cur, cur_clazz, test):
764037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey                error(prev_clazz, test, "Method removed or incompatible change")
765037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
766037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        for test in prev_clazz.fields:
767037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey            if not field_exists(cur, cur_clazz, test):
768037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey                error(prev_clazz, test, "Field removed or incompatible change")
769037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
770037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    return failures
771037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
772037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
7738190f4885b3eb34231877003a583116a0e82826eJeff Sharkeycur = parse_api(sys.argv[1])
774037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkeycur_fail = verify_style(cur)
7758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
7768190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyif len(sys.argv) > 2:
7778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    prev = parse_api(sys.argv[2])
778037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    prev_fail = verify_style(prev)
7798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
7808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    # ignore errors from previous API level
7818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    for p in prev_fail:
7828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey        if p in cur_fail:
7831498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey            del cur_fail[p]
7848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
785037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    # look for compatibility issues
786037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    compat_fail = verify_compat(cur, prev)
787037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
788037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
789037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey    for f in sorted(compat_fail):
790037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        print compat_fail[f]
791037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey        print
792037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey
7938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey
794037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkeyprint "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
7951498f9c615395de17e11204b962d7d925e5f222dJeff Sharkeyfor f in sorted(cur_fail):
7961498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey    print cur_fail[f]
7978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey    print
798