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