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