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 291c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalfimport re, sys, collections, traceback, argparse 308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 328190f4885b3eb34231877003a583116a0e82826eJeff SharkeyBLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) 338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 341c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam MetcalfALLOW_GOOGLE = False 351c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam MetcalfUSE_COLOR = True 361c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 378190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): 388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes 391c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if not USE_COLOR: return "" 408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey codes = [] 418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if reset: codes.append("0") 428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not fg is None: codes.append("3%d" % (fg)) 448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not bg is None: 458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not bright: codes.append("4%d" % (bg)) 468190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: codes.append("10%d" % (bg)) 478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if bold: codes.append("1") 488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey elif dim: codes.append("2") 498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: codes.append("22") 508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey return "\033[%sm" % (";".join(codes)) 518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 538190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Field(): 54ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey def __init__(self, clazz, line, raw, blame): 558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.clazz = clazz 56ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.line = line 578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.raw = raw.strip(" {;") 581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey self.blame = blame 598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey raw = raw.split() 618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.split = list(raw) 628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]: 648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey while r in raw: raw.remove(r) 658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.typ = raw[0] 678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.name = raw[1].strip(";") 688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if len(raw) >= 4 and raw[2] == "=": 698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.value = raw[3].strip(';"') 708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.value = None 728190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 73037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey self.ident = self.raw.replace(" deprecated ", " ") 74037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey def __repr__(self): 768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey return self.raw 778190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 798190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Method(): 80ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey def __init__(self, clazz, line, raw, blame): 818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.clazz = clazz 82ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.line = line 838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.raw = raw.strip(" {;") 841498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey self.blame = blame 851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 861498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey # drop generics for now 871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey raw = re.sub("<.+?>", "", raw) 888190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey raw = re.split("[\s(),;]+", raw) 908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for r in ["", ";"]: 918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey while r in raw: raw.remove(r) 928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.split = list(raw) 938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 94db234ef0ac1d1a7fec7d1f00ba37bbd7435e9893Filip Pavlis for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default"]: 958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey while r in raw: raw.remove(r) 968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.typ = raw[0] 988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.name = raw[1] 998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.args = [] 1008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for r in raw[2:]: 1018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if r == "throws": break 1028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.args.append(r) 1038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 104037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey # identity for compat purposes 105037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey ident = self.raw 106037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey ident = ident.replace(" deprecated ", " ") 107037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey ident = ident.replace(" synchronized ", " ") 108037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey ident = re.sub("<.+?>", "", ident) 109037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if " throws " in ident: 110037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey ident = ident[:ident.index(" throws ")] 111037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey self.ident = ident 112037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey def __repr__(self): 1148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey return self.raw 1158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1178190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Class(): 118ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey def __init__(self, pkg, line, raw, blame): 1198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.pkg = pkg 120ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.line = line 1218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.raw = raw.strip(" {;") 1221498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey self.blame = blame 1238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.ctors = [] 1248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.fields = [] 1258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.methods = [] 1268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey raw = raw.split() 1288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.split = list(raw) 1298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "class" in raw: 1308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.fullname = raw[raw.index("class")+1] 1318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey elif "interface" in raw: 1328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.fullname = raw[raw.index("interface")+1] 133037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey else: 134037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey raise ValueError("Funky class type %s" % (self.raw)) 135037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 136037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if "extends" in raw: 137037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey self.extends = raw[raw.index("extends")+1] 138047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey self.extends_path = self.extends.split(".") 139037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey else: 140037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey self.extends = None 141047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey self.extends_path = [] 1428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1431498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey self.fullname = self.pkg.name + "." + self.fullname 144047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey self.fullname_path = self.fullname.split(".") 145047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey 1461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey self.name = self.fullname[self.fullname.rindex(".")+1:] 1471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 1488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey def __repr__(self): 1498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey return self.raw 1508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1528190f4885b3eb34231877003a583116a0e82826eJeff Sharkeyclass Package(): 153ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey def __init__(self, line, raw, blame): 154ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.line = line 1558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.raw = raw.strip(" {;") 1561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey self.blame = blame 1578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey raw = raw.split() 1598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey self.name = raw[raw.index("package")+1] 160047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey self.name_path = self.name.split(".") 1618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey def __repr__(self): 1638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey return self.raw 1648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1658190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 166a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef _parse_stream(f, clazz_cb=None): 167ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey line = 0 1681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey api = {} 1698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey pkg = None 1708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey clazz = None 1711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey blame = None 1721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 1731498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$") 174a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey for raw in f: 175ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey line += 1 176ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey raw = raw.rstrip() 177ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey match = re_blame.match(raw) 178ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey if match is not None: 179ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey blame = match.groups()[0:2] 180ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey raw = match.groups()[2] 181ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey else: 182ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey blame = None 183ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey 184ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey if raw.startswith("package"): 185ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey pkg = Package(line, raw, blame) 186ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey elif raw.startswith(" ") and raw.endswith("{"): 187a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey # When provided with class callback, we treat as incremental 188a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey # parse and don't build up entire API 189a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz and clazz_cb: 190a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey clazz_cb(clazz) 191ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey clazz = Class(pkg, line, raw, blame) 192a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if not clazz_cb: 193a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey api[clazz.fullname] = clazz 194ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey elif raw.startswith(" ctor"): 195ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey clazz.ctors.append(Method(clazz, line, raw, blame)) 196ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey elif raw.startswith(" method"): 197ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey clazz.methods.append(Method(clazz, line, raw, blame)) 198ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey elif raw.startswith(" field"): 199ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey clazz.fields.append(Field(clazz, line, raw, blame)) 200ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey 201a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey # Handle last trailing class 202a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz and clazz_cb: 203a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey clazz_cb(clazz) 2048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 205a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey return api 2068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 207ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey 208ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkeyclass Failure(): 2099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey def __init__(self, sig, clazz, detail, error, rule, msg): 210ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.sig = sig 211ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.error = error 2129f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey self.rule = rule 213ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.msg = msg 214ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey 215ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey if error: 2169f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey self.head = "Error %s" % (rule) if rule else "Error" 2179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey dump = "%s%s:%s %s" % (format(fg=RED, bg=BLACK, bold=True), self.head, format(reset=True), msg) 218ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey else: 2199f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey self.head = "Warning %s" % (rule) if rule else "Warning" 2209f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey dump = "%s%s:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), self.head, format(reset=True), msg) 221ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey 222ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.line = clazz.line 223ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey blame = clazz.blame 224ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey if detail is not None: 225ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey dump += "\n in " + repr(detail) 226ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.line = detail.line 227ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey blame = detail.blame 228ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey dump += "\n in " + repr(clazz) 229ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey dump += "\n in " + repr(clazz.pkg) 230ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey dump += "\n at line " + repr(self.line) 231ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey if blame is not None: 232ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey dump += "\n last modified by %s in %s" % (blame[1], blame[0]) 233ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey 234ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey self.dump = dump 235ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey 236ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey def __repr__(self): 237ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey return self.dump 2388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2401498f9c615395de17e11204b962d7d925e5f222dJeff Sharkeyfailures = {} 241294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 2429f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef _fail(clazz, detail, error, rule, msg): 2438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Records an API failure to be processed later.""" 2448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey global failures 2458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2461498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg) 2471498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey sig = sig.replace(" deprecated ", " ") 2481498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 2499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey failures[sig] = Failure(sig, clazz, detail, error, rule, msg) 250ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey 2518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2529f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef warn(clazz, detail, rule, msg): 2539f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey _fail(clazz, detail, False, rule, msg) 2548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2559f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef error(clazz, detail, rule, msg): 2569f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey _fail(clazz, detail, True, rule, msg) 2578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2598190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_constants(clazz): 2608190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """All static final constants must be FOO_NAME style.""" 2611498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if re.match("android\.R\.[a-z]+", clazz.fullname): return 26224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if clazz.fullname.startswith("android.os.Build"): return 26324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if clazz.fullname == "android.system.OsConstants": return 2648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 26524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey req = ["java.lang.String","byte","short","int","long","float","double","boolean","char"] 2668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 2678190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "static" in f.split and "final" in f.split: 2688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.match("[A-Z0-9_]+", f.name) is None: 26990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "C2", "Constant field names must be FOO_NAME") 27024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if f.typ != "java.lang.String": 271331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if f.name.startswith("MIN_") or f.name.startswith("MAX_"): 272331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey warn(clazz, f, "C8", "If min/max could change in future, make them dynamic methods") 27324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if f.typ in req and f.value is None: 27424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, f, None, "All constants must be defined at compile time") 2758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2778190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_enums(clazz): 2788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Enums are bad, mmkay?""" 2798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "extends java.lang.Enum" in clazz.raw: 2809f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "F5", "Enums are not allowed") 2818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2838190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_class_names(clazz): 2848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Try catching malformed class names like myMtp or MTPUser.""" 2851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname.startswith("android.opengl"): return 2861498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname.startswith("android.renderscript"): return 2871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if re.match("android\.R\.[a-z]+", clazz.fullname): return 2881498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 2898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.search("[A-Z]{2,}", clazz.name) is not None: 29090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, None, "S1", "Class names with acronyms should be Mtp not MTP") 2918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.match("[^A-Z]", clazz.name): 2929f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "S1", "Class must start with uppercase char") 2938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 2958190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_method_names(clazz): 2968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Try catching malformed method names, like Foo() or getMTU().""" 2971498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname.startswith("android.opengl"): return 2981498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname.startswith("android.renderscript"): return 2991498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.system.OsConstants": return 3008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 3028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.search("[A-Z]{2,}", m.name) is not None: 30390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, m, "S1", "Method names with acronyms should be getMtu() instead of getMTU()") 3048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if re.match("[^a-z]", m.name): 3059f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "S1", "Method name must start with lowercase char") 3068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3088190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_callbacks(clazz): 3098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify Callback classes. 3108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All callback classes must be abstract. 3118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All methods must follow onFoo() naming style.""" 3121498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.speech.tts.SynthesisCallback": return 3138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3148190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name.endswith("Callbacks"): 31590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, "L1", "Callback class names should be singular") 3168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name.endswith("Observer"): 3179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, None, "L1", "Class should be named FooCallback") 3188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name.endswith("Callback"): 3208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "interface" in clazz.split: 32190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, "CL3", "Callbacks must be abstract class to enable extension in future API levels") 3228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 3248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not re.match("on[A-Z][a-z]*", m.name): 3259f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L1", "Callback method names must be onFoo() style") 3268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3288190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_listeners(clazz): 3298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify Listener classes. 3308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All Listener classes must be interface. 3318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All methods must follow onFoo() naming style. 3328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey If only a single method, it must match class name: 3338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey interface OnFooListener { void onFoo() }""" 3348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name.endswith("Listener"): 3368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if " abstract class " in clazz.raw: 33790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback") 3388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 3408190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not re.match("on[A-Z][a-z]*", m.name): 3419f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L1", "Listener method names must be onFoo() style") 3428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3438190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if len(clazz.methods) == 1 and clazz.name.startswith("On"): 3448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey m = clazz.methods[0] 3458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if (m.name + "Listener").lower() != clazz.name.lower(): 34690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "L1", "Single listener method name must match class name") 3478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3498190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_actions(clazz): 3508190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify intent actions. 3518190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All action names must be named ACTION_FOO. 3528190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All action values must be scoped by package and match name: 3538190f4885b3eb34231877003a583116a0e82826eJeff Sharkey package android.foo { 3548190f4885b3eb34231877003a583116a0e82826eJeff Sharkey String ACTION_BAR = "android.foo.action.BAR"; 3558190f4885b3eb34231877003a583116a0e82826eJeff Sharkey }""" 3568190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 3578190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.value is None: continue 3588190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.name.startswith("EXTRA_"): continue 3591498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.name == "SERVICE_INTERFACE" or f.name == "PROVIDER_INTERFACE": continue 36024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if "INTERACTION" in f.name: continue 3618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3628190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "static" in f.split and "final" in f.split and f.typ == "java.lang.String": 3638190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower(): 3648190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not f.name.startswith("ACTION_"): 3659f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C3", "Intent action constant name must be ACTION_FOO") 3668190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 3671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.content.Intent": 3688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = "android.intent.action" 3691498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.fullname == "android.provider.Settings": 3708190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = "android.settings" 3711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.fullname == "android.app.admin.DevicePolicyManager" or clazz.fullname == "android.app.admin.DeviceAdminReceiver": 3721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey prefix = "android.app.action" 3738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 3748190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = clazz.pkg.name + ".action" 3758190f4885b3eb34231877003a583116a0e82826eJeff Sharkey expected = prefix + "." + f.name[7:] 3768190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.value != expected: 3779f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C4", "Inconsistent action value; expected %s" % (expected)) 3788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3808190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_extras(clazz): 3818190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify intent extras. 3828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All extra names must be named EXTRA_FOO. 3838190f4885b3eb34231877003a583116a0e82826eJeff Sharkey All extra values must be scoped by package and match name: 3848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey package android.foo { 3858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey String EXTRA_BAR = "android.foo.extra.BAR"; 3868190f4885b3eb34231877003a583116a0e82826eJeff Sharkey }""" 3871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.app.Notification": return 3881498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.appwidget.AppWidgetManager": return 3891498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 3908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 3918190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.value is None: continue 3928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.name.startswith("ACTION_"): continue 3938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 3948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "static" in f.split and "final" in f.split and f.typ == "java.lang.String": 3958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower(): 3968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not f.name.startswith("EXTRA_"): 3979f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C3", "Intent extra must be EXTRA_FOO") 3988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 3991498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.pkg.name == "android.content" and clazz.name == "Intent": 4008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = "android.intent.extra" 4011498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.pkg.name == "android.app.admin": 4021498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey prefix = "android.app.extra" 4038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 4048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey prefix = clazz.pkg.name + ".extra" 4058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey expected = prefix + "." + f.name[6:] 4068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if f.value != expected: 4079f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C4", "Inconsistent extra value; expected %s" % (expected)) 4088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4108190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_equals(clazz): 4118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify that equals() and hashCode() must be overridden together.""" 41240d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey eq = False 41340d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey hc = False 41440d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey for m in clazz.methods: 41540d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey if " static " in m.raw: continue 41640d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey if "boolean equals(java.lang.Object)" in m.raw: eq = True 41740d623e676daff9f60ad6ffa6c723a7640eece04Jeff Sharkey if "int hashCode()" in m.raw: hc = True 4188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if eq != hc: 4199f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "M8", "Must override both equals and hashCode; missing one") 4208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4228190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_parcelable(clazz): 4238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify that Parcelable objects aren't hiding required bits.""" 4248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "implements android.os.Parcelable" in clazz.raw: 4258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey creator = [ i for i in clazz.fields if i.name == "CREATOR" ] 4268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey write = [ i for i in clazz.methods if i.name == "writeToParcel" ] 4278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey describe = [ i for i in clazz.methods if i.name == "describeContents" ] 4288190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if len(creator) == 0 or len(write) == 0 or len(describe) == 0: 4309f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one") 4318190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 432331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if " final class " not in clazz.raw: 433331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey error(clazz, None, "FW8", "Parcelable classes must be final") 434331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 4358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4368190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_protected(clazz): 437b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey """Verify that no protected methods or fields are allowed.""" 4388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 4398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "protected" in m.split: 44090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M7", "Protected methods not allowed; must be public") 4418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 4428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "protected" in f.split: 44390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "M7", "Protected fields not allowed; must be public") 4448190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4458190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4468190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_fields(clazz): 4478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify that all exposed fields are final. 4488190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Exposed fields must follow myName style. 4498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Catch internal mFoo objects being exposed.""" 4501498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 4511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey IGNORE_BARE_FIELDS = [ 4521498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.app.ActivityManager.RecentTaskInfo", 4531498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.app.Notification", 4541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.ActivityInfo", 4551498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.ApplicationInfo", 45624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "android.content.pm.ComponentInfo", 45724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "android.content.pm.ResolveInfo", 4581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.FeatureGroupInfo", 4591498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.InstrumentationInfo", 4601498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.PackageInfo", 4611498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.content.pm.PackageItemInfo", 46224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "android.content.res.Configuration", 46324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "android.graphics.BitmapFactory.Options", 4641498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.os.Message", 4651498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey "android.system.StructPollfd", 4661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey ] 4671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 4688190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for f in clazz.fields: 4698190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not "final" in f.split: 4701498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname in IGNORE_BARE_FIELDS: 4711498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey pass 4721498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.fullname.endswith("LayoutParams"): 4731498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey pass 4741498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey elif clazz.fullname.startswith("android.util.Mutable"): 4751498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey pass 4761498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey else: 4779f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable") 4788190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4798190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not "static" in f.split: 4808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not re.match("[a-z]([a-zA-Z]+)?", f.name): 48190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "S1", "Non-static fields must be named using myField style") 4828190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4831498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if re.match("[ms][A-Z]", f.name): 48490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "F1", "Internal objects must not be exposed") 4858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4861498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if re.match("[A-Z_]+", f.name): 4871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if "static" not in f.split or "final" not in f.split: 4889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C2", "Constants must be marked static final") 4891498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 4908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 4918190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_register(clazz): 4928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify parity of registration methods. 4938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Callback objects use register/unregister methods. 4948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Listener objects use add/remove methods.""" 4958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey methods = [ m.name for m in clazz.methods ] 4968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 4978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "Callback" in m.raw: 4988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("register"): 4998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey other = "unregister" + m.name[8:] 5008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if other not in methods: 5019f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L2", "Missing unregister method") 5028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("unregister"): 5038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey other = "register" + m.name[10:] 5048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if other not in methods: 5059f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L2", "Missing register method") 5068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("add") or m.name.startswith("remove"): 5089f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L3", "Callback methods should be named register/unregister") 5098190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5108190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "Listener" in m.raw: 5118190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("add"): 5128190f4885b3eb34231877003a583116a0e82826eJeff Sharkey other = "remove" + m.name[3:] 5138190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if other not in methods: 5149f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L2", "Missing remove method") 5158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("remove") and not m.name.startswith("removeAll"): 5168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey other = "add" + m.name[6:] 5178190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if other not in methods: 5189f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L2", "Missing add method") 5198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5208190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("register") or m.name.startswith("unregister"): 5219f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "L3", "Listener methods should be named add/remove") 5228190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5238190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5248190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_sync(clazz): 5258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify synchronized methods aren't exposed.""" 5268190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 5278190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "synchronized" in m.split: 52890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M5", "Internal locks must not be exposed") 5298190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5308190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5318190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_intent_builder(clazz): 5328190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify that Intent builders are createFooIntent() style.""" 5338190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name == "Intent": return 5348190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5358190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 5368190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.typ == "android.content.Intent": 5378190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("create") and m.name.endswith("Intent"): 5388190f4885b3eb34231877003a583116a0e82826eJeff Sharkey pass 5398190f4885b3eb34231877003a583116a0e82826eJeff Sharkey else: 540539ea12810f6373eed926d07b856ab76a8ae8355Adam Powell warn(clazz, m, "FW1", "Methods creating an Intent should be named createFooIntent()") 5418190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5428190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5438190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_helper_classes(clazz): 544294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey """Verify that helper classes are named consistently with what they extend. 545294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey All developer extendable methods should be named onFoo().""" 546294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = False 5478190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "extends android.app.Service" in clazz.raw: 548294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = True 5498190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not clazz.name.endswith("Service"): 5509f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "CL4", "Inconsistent class name; should be FooService") 5511498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 5521498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey found = False 5531498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey for f in clazz.fields: 5541498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.name == "SERVICE_INTERFACE": 5551498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey found = True 5561498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.value != clazz.fullname: 5579f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname)) 5581498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 5598190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "extends android.content.ContentProvider" in clazz.raw: 560294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = True 5618190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not clazz.name.endswith("Provider"): 5629f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider") 5631498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 5641498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey found = False 5651498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey for f in clazz.fields: 5661498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.name == "PROVIDER_INTERFACE": 5671498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey found = True 5681498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if f.value != clazz.fullname: 56990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "C4", "Inconsistent interface constant; expected %s" % (clazz.fullname)) 5701498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 5718190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if "extends android.content.BroadcastReceiver" in clazz.raw: 572294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = True 5738190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not clazz.name.endswith("Receiver"): 5749f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver") 5751498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 576294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if "extends android.app.Activity" in clazz.raw: 577294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey test_methods = True 578294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if not clazz.name.endswith("Activity"): 5799f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity") 580294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 581294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if test_methods: 582294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for m in clazz.methods: 583294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if "final" in m.split: continue 584294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if not re.match("on[A-Z]", m.name): 5851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if "abstract" in m.split: 586b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey warn(clazz, m, None, "Methods implemented by developers should be named onFoo()") 5871498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey else: 5889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, m, None, "If implemented by developer, should be named onFoo(); otherwise consider marking final") 5898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5908190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5918190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_builder(clazz): 5928190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Verify builder classes. 5938190f4885b3eb34231877003a583116a0e82826eJeff Sharkey Methods should return the builder to enable chaining.""" 5948190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if " extends " in clazz.raw: return 5958190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not clazz.name.endswith("Builder"): return 5968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 5978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if clazz.name != "Builder": 5989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, None, None, "Builder should be defined as inner class") 5998190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6008190f4885b3eb34231877003a583116a0e82826eJeff Sharkey has_build = False 6018190f4885b3eb34231877003a583116a0e82826eJeff Sharkey for m in clazz.methods: 6028190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name == "build": 6038190f4885b3eb34231877003a583116a0e82826eJeff Sharkey has_build = True 6048190f4885b3eb34231877003a583116a0e82826eJeff Sharkey continue 6058190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6068190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("get"): continue 6078190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if m.name.startswith("clear"): continue 6088190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 609294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if m.name.startswith("with"): 61090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, m, None, "Builder methods names should use setFoo() style") 611294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 612294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if m.name.startswith("set"): 613294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if not m.typ.endswith(clazz.fullname): 61490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, m, "M4", "Methods must return the builder object") 6158190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6168190f4885b3eb34231877003a583116a0e82826eJeff Sharkey if not has_build: 6179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, None, None, "Missing build() method") 6188190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6198190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6208190f4885b3eb34231877003a583116a0e82826eJeff Sharkeydef verify_aidl(clazz): 6218190f4885b3eb34231877003a583116a0e82826eJeff Sharkey """Catch people exposing raw AIDL.""" 622932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw: 62390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, None, "Raw AIDL interfaces must not be exposed") 6248190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 6258190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 626932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_internal(clazz): 627932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey """Catch people exposing internal classes.""" 628932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if clazz.pkg.name.startswith("com.android"): 62990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, None, None, "Internal classes must not be exposed") 630932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 631932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 632932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkeydef verify_layering(clazz): 633932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey """Catch package layering violations. 634932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey For example, something in android.os depending on android.app.""" 635932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ranking = [ 636932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"], 637932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.app", 638932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.widget", 639932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.view", 640932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.animation", 641932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.provider", 6421498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey ["android.content","android.graphics.drawable"], 643932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.database", 644932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.graphics", 645932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.text", 646932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.os", 647932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey "android.util" 648932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ] 649932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 650932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey def rank(p): 651932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for i in range(len(ranking)): 652932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if isinstance(ranking[i], list): 653932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for j in ranking[i]: 654932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if p.startswith(j): return i 655932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey else: 656932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if p.startswith(ranking[i]): return i 657932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 658932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey cr = rank(clazz.pkg.name) 659932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if cr is None: return 660932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 661932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for f in clazz.fields: 662932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ir = rank(f.typ) 663932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if ir and ir < cr: 6649f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, f, "FW6", "Field type violates package layering") 665932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 666932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for m in clazz.methods: 667932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ir = rank(m.typ) 668932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if ir and ir < cr: 6699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, m, "FW6", "Method return type violates package layering") 670932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey for arg in m.args: 671932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey ir = rank(arg) 672932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey if ir and ir < cr: 6739f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey warn(clazz, m, "FW6", "Method argument type violates package layering") 674932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 675932a07cefea64e858fc999da4be577b10b59fd9fJeff Sharkey 676a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef verify_boolean(clazz): 67790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey """Verifies that boolean accessors are named correctly. 67890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey For example, hasFoo() and setHasFoo().""" 6791498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 68090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey def is_get(m): return len(m.args) == 0 and m.typ == "boolean" 68190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey def is_set(m): return len(m.args) == 1 and m.args[0] == "boolean" 68290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 68390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey gets = [ m for m in clazz.methods if is_get(m) ] 68490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey sets = [ m for m in clazz.methods if is_set(m) ] 6851498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 68690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey def error_if_exists(methods, trigger, expected, actual): 68790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for m in methods: 68890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if m.name == actual: 68990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M6", "Symmetric method for %s must be named %s" % (trigger, expected)) 6901498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 691294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for m in clazz.methods: 69290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if is_get(m): 69390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("is[A-Z]", m.name): 69490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey target = m.name[2:] 69590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey expected = "setIs" + target 69690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "setHas" + target) 69790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey elif re.match("has[A-Z]", m.name): 69890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey target = m.name[3:] 69990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey expected = "setHas" + target 70090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "setIs" + target) 70190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "set" + target) 70290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey elif re.match("get[A-Z]", m.name): 70390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey target = m.name[3:] 70490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey expected = "set" + target 70590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "setIs" + target) 70690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "setHas" + target) 70790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 70890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if is_set(m): 70990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("set[A-Z]", m.name): 71090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey target = m.name[3:] 71190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey expected = "get" + target 71290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "is" + target) 71390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error_if_exists(sets, m.name, expected, "has" + target) 714294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 715294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 716294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_collections(clazz): 717294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey """Verifies that collection types are interfaces.""" 7181498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if clazz.fullname == "android.os.Bundle": return 7191498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey 720294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey bad = ["java.util.Vector", "java.util.LinkedList", "java.util.ArrayList", "java.util.Stack", 721294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"] 722294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for m in clazz.methods: 7231498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if m.typ in bad: 72490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "CL2", "Return type is concrete collection; must be higher-level interface") 725294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for arg in m.args: 7261498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey if arg in bad: 72790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "CL2", "Argument is concrete collection; must be higher-level interface") 728294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 729294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 730294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkeydef verify_flags(clazz): 731294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey """Verifies that flags are non-overlapping.""" 732294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey known = collections.defaultdict(int) 733294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey for f in clazz.fields: 734294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if "FLAG_" in f.name: 735294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey try: 736294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey val = int(f.value) 737294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey except: 738294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey continue 739294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 740294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey scope = f.name[0:f.name.index("FLAG_")] 741294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey if val & known[scope]: 742b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey warn(clazz, f, "C1", "Found overlapping flag constant value") 743294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey known[scope] |= val 744294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 745294f0de15b510afc06a436bf7cd45d99512c71d3Jeff Sharkey 7469f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_exception(clazz): 7479f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that methods don't throw generic exceptions.""" 7489f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for m in clazz.methods: 7499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if "throws java.lang.Exception" in m.raw or "throws java.lang.Throwable" in m.raw or "throws java.lang.Error" in m.raw: 7509f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, "S1", "Methods must not throw generic exceptions") 7519f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 752331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if "throws android.os.RemoteException" in m.raw: 753331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if clazz.name == "android.content.ContentProviderClient": continue 754331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if clazz.name == "android.os.Binder": continue 755331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if clazz.name == "android.os.IBinder": continue 756331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 757331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey error(clazz, m, "FW9", "Methods calling into system server should rethrow RemoteException as RuntimeException") 758331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 7599f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7609f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_google(clazz): 7619f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that APIs never reference Google.""" 7629f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7639f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if re.search("google", clazz.raw, re.IGNORECASE): 7649f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, None, "Must never reference Google") 7659f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7669f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey test = [] 7679f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey test.extend(clazz.ctors) 7689f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey test.extend(clazz.fields) 7699f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey test.extend(clazz.methods) 7709f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7719f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for t in test: 7729f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if re.search("google", t.raw, re.IGNORECASE): 7739f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, t, None, "Must never reference Google") 7749f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7759f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7769f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_bitset(clazz): 7779f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that we avoid using heavy BitSet.""" 7789f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7799f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for f in clazz.fields: 7809f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if f.typ == "java.util.BitSet": 7819f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, f, None, "Field type must not be heavy BitSet") 7829f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7839f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for m in clazz.methods: 7849f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if m.typ == "java.util.BitSet": 7859f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, None, "Return type must not be heavy BitSet") 7869f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for arg in m.args: 7879f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if arg == "java.util.BitSet": 7889f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, m, None, "Argument type must not be heavy BitSet") 7899f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7909f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7919f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_manager(clazz): 7929f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that FooManager is only obtained from Context.""" 7939f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7949f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if not clazz.name.endswith("Manager"): return 7959f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 7969f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for c in clazz.ctors: 797047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey error(clazz, c, None, "Managers must always be obtained from Context; no direct constructors") 7989f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 79924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey for m in clazz.methods: 80024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if m.typ == clazz.fullname: 80124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, m, None, "Managers must always be obtained from Context") 80224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 8039f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8049f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_boxed(clazz): 8059f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that methods avoid boxed primitives.""" 8069f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 807b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey boxed = ["java.lang.Number","java.lang.Byte","java.lang.Double","java.lang.Float","java.lang.Integer","java.lang.Long","java.lang.Short"] 8089f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8099f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for c in clazz.ctors: 8109f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for arg in c.args: 8119f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if arg in boxed: 81290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, c, "M11", "Must avoid boxed primitives") 8139f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8149f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for f in clazz.fields: 8159f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if f.typ in boxed: 81690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "M11", "Must avoid boxed primitives") 8179f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8189f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for m in clazz.methods: 8199f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if m.typ in boxed: 82090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M11", "Must avoid boxed primitives") 8219f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey for arg in m.args: 8229f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if arg in boxed: 82390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, m, "M11", "Must avoid boxed primitives") 8249f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8259f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8269f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkeydef verify_static_utils(clazz): 8279f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey """Verifies that helper classes can't be constructed.""" 8289f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey if clazz.fullname.startswith("android.opengl"): return 82924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if clazz.fullname.startswith("android.R"): return 8309f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 83124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey # Only care about classes with default constructors 83224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if len(clazz.ctors) == 1 and len(clazz.ctors[0].args) == 0: 83324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey test = [] 83424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey test.extend(clazz.fields) 83524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey test.extend(clazz.methods) 8369f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 83724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if len(test) == 0: return 83824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey for t in test: 83924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if "static" not in t.split: 84024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey return 8419f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8429f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(clazz, None, None, "Fully-static utility classes must not have constructor") 8439f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 8449f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey 845b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_overload_args(clazz): 846b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey """Verifies that method overloads add new arguments at the end.""" 847b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if clazz.fullname.startswith("android.opengl"): return 848b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 849b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey overloads = collections.defaultdict(list) 850b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in clazz.methods: 851b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if "deprecated" in m.split: continue 852b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey overloads[m.name].append(m) 853b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 854a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey for name, methods in overloads.items(): 855b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if len(methods) <= 1: continue 856b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 857b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey # Look for arguments common across all overloads 858b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey def cluster(args): 859b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey count = collections.defaultdict(int) 860b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey res = set() 861b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for i in range(len(args)): 862b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey a = args[i] 863b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey res.add("%s#%d" % (a, count[a])) 864b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey count[a] += 1 865b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey return res 866b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 867b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey common_args = cluster(methods[0].args) 868b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in methods: 869b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey common_args = common_args & cluster(m.args) 870b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 871b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if len(common_args) == 0: continue 872b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 873b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey # Require that all common arguments are present at start of signature 874b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey locked_sig = None 875b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in methods: 876b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey sig = m.args[0:len(common_args)] 877b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if not common_args.issubset(cluster(sig)): 878b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey warn(clazz, m, "M2", "Expected common arguments [%s] at beginning of overloaded method" % (", ".join(common_args))) 879b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey elif not locked_sig: 880b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey locked_sig = sig 881b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey elif locked_sig != sig: 882b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey error(clazz, m, "M2", "Expected consistent argument ordering between overloads: %s..." % (", ".join(locked_sig))) 883b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 884b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 885b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_callback_handlers(clazz): 886b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey """Verifies that methods adding listener/callback have overload 887b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for specifying delivery thread.""" 888b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 889047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey # Ignore UI packages which assume main thread 890b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey skip = [ 891047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "animation", 892047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "view", 893047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "graphics", 894047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "transition", 895047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "widget", 896047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey "webkit", 897b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey ] 898b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for s in skip: 899047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if s in clazz.pkg.name_path: return 900047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if s in clazz.extends_path: return 901047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey 902047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey # Ignore UI classes which assume main thread 903047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if "app" in clazz.pkg.name_path or "app" in clazz.extends_path: 904047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey for s in ["ActionBar","Dialog","Application","Activity","Fragment","Loader"]: 905047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if s in clazz.fullname: return 906047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if "content" in clazz.pkg.name_path or "content" in clazz.extends_path: 907047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey for s in ["Loader"]: 908047d7f0c6ded6fb6b01a708070955647b7ff4ae7Jeff Sharkey if s in clazz.fullname: return 909b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 910b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey found = {} 911b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey by_name = collections.defaultdict(list) 912b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in clazz.methods: 913b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if m.name.startswith("unregister"): continue 914b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if m.name.startswith("remove"): continue 915b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if re.match("on[A-Z]+", m.name): continue 916b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 917b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey by_name[m.name].append(m) 918b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 919b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for a in m.args: 920b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if a.endswith("Listener") or a.endswith("Callback") or a.endswith("Callbacks"): 921b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey found[m.name] = m 922b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 923b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for f in found.values(): 924b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey takes_handler = False 925b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey for m in by_name[f.name]: 926b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if "android.os.Handler" in m.args: 927b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey takes_handler = True 928b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if not takes_handler: 92990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler") 930b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 931b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 932b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkeydef verify_context_first(clazz): 933b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey """Verifies that methods accepting a Context keep it the first argument.""" 93490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey examine = clazz.ctors + clazz.methods 93590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for m in examine: 93690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if len(m.args) > 1 and m.args[0] != "android.content.Context": 937b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey if "android.content.Context" in m.args[1:]: 938b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey error(clazz, m, "M3", "Context is distinct, so it must be the first argument") 93924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if len(m.args) > 1 and m.args[0] != "android.content.ContentResolver": 94024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if "android.content.ContentResolver" in m.args[1:]: 94124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, m, "M3", "ContentResolver is distinct, so it must be the first argument") 942b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 94390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 94490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkeydef verify_listener_last(clazz): 94590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey """Verifies that methods accepting a Listener or Callback keep them as last arguments.""" 94690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey examine = clazz.ctors + clazz.methods 94790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for m in examine: 94890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if "Listener" in m.name or "Callback" in m.name: continue 94990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey found = False 95090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for a in m.args: 95190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"): 95290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey found = True 95390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey elif found and a != "android.os.Handler": 95490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey warn(clazz, m, "M3", "Listeners should always be at end of argument list") 95590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 95690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 95790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkeydef verify_resource_names(clazz): 95890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey """Verifies that resource names have consistent case.""" 95990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if not re.match("android\.R\.[a-z]+", clazz.fullname): return 96090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 96190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey # Resources defined by files are foo_bar_baz 96290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]: 96390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for f in clazz.fields: 96490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("[a-z1-9_]+$", f.name): continue 96590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style") 96690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 96790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey # Resources defined inside files are fooBarBaz 96890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if clazz.name in ["array","attr","id","bool","fraction","integer"]: 96990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for f in clazz.fields: 97090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue 97190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("layout_[a-z][a-zA-Z1-9]*$", f.name): continue 97290b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("state_[a-z_]*$", f.name): continue 97390b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 97490b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("[a-z][a-zA-Z1-9]*$", f.name): continue 97590b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "C7", "Expected resource name in this class to be fooBarBaz style") 97690b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey 97790b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey # Styles are FooBar_Baz 97890b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if clazz.name in ["style"]: 97990b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey for f in clazz.fields: 98090b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey if re.match("[A-Z][A-Za-z1-9]+(_[A-Z][A-Za-z1-9]+?)*$", f.name): continue 98190b547bb50b3ceaa78e8832fa8daeea851802928Jeff Sharkey error(clazz, f, "C7", "Expected resource name in this class to be FooBar_Baz style") 982b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 983b46a9690d54c677adeadb93e830da42c6a75f09cJeff Sharkey 984331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkeydef verify_files(clazz): 985331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey """Verifies that methods accepting File also accept streams.""" 986331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 987331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey has_file = set() 988331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey has_stream = set() 989331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 990331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey test = [] 991331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey test.extend(clazz.ctors) 992331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey test.extend(clazz.methods) 993331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 994331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey for m in test: 995331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if "java.io.File" in m.args: 996331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey has_file.add(m) 997331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if "java.io.FileDescriptor" in m.args or "android.os.ParcelFileDescriptor" in m.args or "java.io.InputStream" in m.args or "java.io.OutputStream" in m.args: 998331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey has_stream.add(m.name) 999331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 1000331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey for m in has_file: 1001331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if m.name not in has_stream: 1002331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey warn(clazz, m, "M10", "Methods accepting File should also accept FileDescriptor or streams") 1003331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 1004331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey 100570168dde6e1da06042818350fc6e258188d001aeJeff Sharkeydef verify_manager_list(clazz): 100670168dde6e1da06042818350fc6e258188d001aeJeff Sharkey """Verifies that managers return List<? extends Parcelable> instead of arrays.""" 100770168dde6e1da06042818350fc6e258188d001aeJeff Sharkey 100870168dde6e1da06042818350fc6e258188d001aeJeff Sharkey if not clazz.name.endswith("Manager"): return 100970168dde6e1da06042818350fc6e258188d001aeJeff Sharkey 101070168dde6e1da06042818350fc6e258188d001aeJeff Sharkey for m in clazz.methods: 101170168dde6e1da06042818350fc6e258188d001aeJeff Sharkey if m.typ.startswith("android.") and m.typ.endswith("[]"): 101270168dde6e1da06042818350fc6e258188d001aeJeff Sharkey warn(clazz, m, None, "Methods should return List<? extends Parcelable> instead of Parcelable[] to support ParceledListSlice under the hood") 101370168dde6e1da06042818350fc6e258188d001aeJeff Sharkey 101470168dde6e1da06042818350fc6e258188d001aeJeff Sharkey 101526c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkeydef verify_abstract_inner(clazz): 101626c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey """Verifies that abstract inner classes are static.""" 101726c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey 101826c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey if re.match(".+?\.[A-Z][^\.]+\.[A-Z]", clazz.fullname): 101926c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey if " abstract " in clazz.raw and " static " not in clazz.raw: 102026c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey warn(clazz, None, None, "Abstract inner classes should be static to improve testability") 102126c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey 102226c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey 102324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkeydef verify_runtime_exceptions(clazz): 102424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey """Verifies that runtime exceptions aren't listed in throws.""" 102524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 102624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey banned = [ 102724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.NullPointerException", 102824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.ClassCastException", 102924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.IndexOutOfBoundsException", 103024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.reflect.UndeclaredThrowableException", 103124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.reflect.MalformedParametersException", 103224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.reflect.MalformedParameterizedTypeException", 103324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.invoke.WrongMethodTypeException", 103424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.EnumConstantNotPresentException", 103524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.IllegalMonitorStateException", 103624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.SecurityException", 103724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.UnsupportedOperationException", 103824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.annotation.AnnotationTypeMismatchException", 103924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.annotation.IncompleteAnnotationException", 104024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.TypeNotPresentException", 104124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.IllegalStateException", 104224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.ArithmeticException", 104324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.IllegalArgumentException", 104424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.ArrayStoreException", 104524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.lang.NegativeArraySizeException", 104624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.util.MissingResourceException", 104724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.util.EmptyStackException", 104824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.util.concurrent.CompletionException", 104924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.util.concurrent.RejectedExecutionException", 105024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.util.IllformedLocaleException", 105124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.util.ConcurrentModificationException", 105224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.util.NoSuchElementException", 105324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.io.UncheckedIOException", 105424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.time.DateTimeException", 105524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.security.ProviderException", 105624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.nio.BufferUnderflowException", 105724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "java.nio.BufferOverflowException", 105824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey ] 105924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 106024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey test = [] 106124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey test.extend(clazz.ctors) 106224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey test.extend(clazz.methods) 106324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 106424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey for t in test: 106524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if " throws " not in t.raw: continue 106624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey throws = t.raw[t.raw.index(" throws "):] 106724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey for b in banned: 106824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if b in throws: 106924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, t, None, "Methods must not mention RuntimeException subclasses in throws clauses") 107024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 107124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 107224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkeydef verify_error(clazz): 107324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey """Verifies that we always use Exception instead of Error.""" 107424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if not clazz.extends: return 107524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if clazz.extends.endswith("Error"): 107624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, None, None, "Trouble must be reported through an Exception, not Error") 107724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if clazz.extends.endswith("Exception") and not clazz.name.endswith("Exception"): 107824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, None, None, "Exceptions must be named FooException") 107924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 108024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 108124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkeydef verify_units(clazz): 108224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey """Verifies that we use consistent naming for units.""" 108324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 108424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey # If we find K, recommend replacing with V 108524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey bad = { 108624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "Ns": "Nanos", 108724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "Ms": "Millis or Micros", 108824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "Sec": "Seconds", "Secs": "Seconds", 108924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "Hr": "Hours", "Hrs": "Hours", 109024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "Mo": "Months", "Mos": "Months", 109124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "Yr": "Years", "Yrs": "Years", 109224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey "Byte": "Bytes", "Space": "Bytes", 109324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey } 109424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 109524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey for m in clazz.methods: 109624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if m.typ not in ["short","int","long"]: continue 109724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey for k, v in bad.iteritems(): 109824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if m.name.endswith(k): 109924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, m, None, "Expected method name units to be " + v) 110024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if m.name.endswith("Nanos") or m.name.endswith("Micros"): 110124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey warn(clazz, m, None, "Returned time values are strongly encouraged to be in milliseconds unless you need the extra precision") 110224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if m.name.endswith("Seconds"): 110324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, m, None, "Returned time values must be in milliseconds") 110424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 110524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey for m in clazz.methods: 110624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey typ = m.typ 110724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if typ == "void": 110824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if len(m.args) != 1: continue 110924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey typ = m.args[0] 111024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 111124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if m.name.endswith("Fraction") and typ != "float": 111224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, m, None, "Fractions must use floats") 111324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if m.name.endswith("Percentage") and typ != "int": 111424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey error(clazz, m, None, "Percentage must use ints") 111524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 111624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 111724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkeydef verify_closable(clazz): 111824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey """Verifies that classes are AutoClosable.""" 111924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if "implements java.lang.AutoCloseable" in clazz.raw: return 112024bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if "implements java.io.Closeable" in clazz.raw: return 112124bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 112224bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey for m in clazz.methods: 112324bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if len(m.args) > 0: continue 112424bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey if m.name in ["close","release","destroy","finish","finalize","disconnect","shutdown","stop","free","quit"]: 112524bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey warn(clazz, m, None, "Classes that release resources should implement AutoClosable and CloseGuard") 112624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey return 112724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 112824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey 1129a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_clazz(clazz): 1130a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """Find all style issues in the given class.""" 1131a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("java"): return 1132a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("junit"): return 1133a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("org.apache"): return 1134a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("org.xml"): return 1135a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("org.json"): return 1136a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey if clazz.pkg.name.startswith("org.w3c"): return 1137331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey if clazz.pkg.name.startswith("android.icu."): return 1138a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey 1139a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_constants(clazz) 1140a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_enums(clazz) 1141a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_class_names(clazz) 1142a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_method_names(clazz) 1143a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_callbacks(clazz) 1144a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_listeners(clazz) 1145a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_actions(clazz) 1146a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_extras(clazz) 1147a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_equals(clazz) 1148a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_parcelable(clazz) 1149a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_protected(clazz) 1150a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_fields(clazz) 1151a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_register(clazz) 1152a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_sync(clazz) 1153a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_intent_builder(clazz) 1154a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_helper_classes(clazz) 1155a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_builder(clazz) 1156a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_aidl(clazz) 1157a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_internal(clazz) 1158a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_layering(clazz) 1159a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_boolean(clazz) 1160a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_collections(clazz) 1161a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_flags(clazz) 1162a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_exception(clazz) 11631c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if not ALLOW_GOOGLE: verify_google(clazz) 1164a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_bitset(clazz) 1165a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_manager(clazz) 1166a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_boxed(clazz) 1167a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_static_utils(clazz) 1168a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_overload_args(clazz) 1169a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_callback_handlers(clazz) 1170a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_context_first(clazz) 1171a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_listener_last(clazz) 1172a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey verify_resource_names(clazz) 1173331279b239a6f5f392372f34cd6923fa589f9987Jeff Sharkey verify_files(clazz) 117470168dde6e1da06042818350fc6e258188d001aeJeff Sharkey verify_manager_list(clazz) 117526c809064747d62a139f33c3c5124102cbba7e83Jeff Sharkey verify_abstract_inner(clazz) 117624bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey verify_runtime_exceptions(clazz) 117724bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey verify_error(clazz) 117824bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey verify_units(clazz) 117924bda1d6c82451cefb8eba50c5356089effb2c91Jeff Sharkey verify_closable(clazz) 1180a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey 1181a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey 1182a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_stream(stream): 1183a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """Find all style issues in the given API stream.""" 11848190f4885b3eb34231877003a583116a0e82826eJeff Sharkey global failures 1185a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey failures = {} 1186a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey _parse_stream(stream, examine_clazz) 1187a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey return failures 1188a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey 11898190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1190a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkeydef examine_api(api): 1191a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """Find all style issues in the given parsed API.""" 1192a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey global failures 11931498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey failures = {} 11941498f9c615395de17e11204b962d7d925e5f222dJeff Sharkey for key in sorted(api.keys()): 1195a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey examine_clazz(api[key]) 11968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey return failures 11978190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 11988190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1199037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkeydef verify_compat(cur, prev): 1200037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey """Find any incompatible API changes between two levels.""" 1201037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey global failures 1202037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1203037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def class_exists(api, test): 1204037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return test.fullname in api 1205037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1206037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def ctor_exists(api, clazz, test): 1207037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for m in clazz.ctors: 1208037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if m.ident == test.ident: return True 1209037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return False 1210037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1211037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def all_methods(api, clazz): 1212037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey methods = list(clazz.methods) 1213037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if clazz.extends is not None: 1214037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey methods.extend(all_methods(api, api[clazz.extends])) 1215037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return methods 1216037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1217037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def method_exists(api, clazz, test): 1218037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey methods = all_methods(api, clazz) 1219037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for m in methods: 1220037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if m.ident == test.ident: return True 1221037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return False 1222037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1223037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey def field_exists(api, clazz, test): 1224037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for f in clazz.fields: 1225037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if f.ident == test.ident: return True 1226037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return False 1227037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1228037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey failures = {} 1229037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for key in sorted(prev.keys()): 1230037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey prev_clazz = prev[key] 1231037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1232037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if not class_exists(cur, prev_clazz): 12339f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(prev_clazz, None, None, "Class removed or incompatible change") 1234037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey continue 1235037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1236037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey cur_clazz = cur[key] 1237037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1238037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for test in prev_clazz.ctors: 1239037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if not ctor_exists(cur, cur_clazz, test): 12409f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(prev_clazz, prev_ctor, None, "Constructor removed or incompatible change") 1241037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1242037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey methods = all_methods(prev, prev_clazz) 1243037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for test in methods: 1244037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if not method_exists(cur, cur_clazz, test): 12459f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(prev_clazz, test, None, "Method removed or incompatible change") 1246037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1247037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey for test in prev_clazz.fields: 1248037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey if not field_exists(cur, cur_clazz, test): 12499f64d5c6c456863489ffb8583d611211e7c2f88fJeff Sharkey error(prev_clazz, test, None, "Field removed or incompatible change") 1250037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1251037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey return failures 1252037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1253037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1254ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkeyif __name__ == "__main__": 12551c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser = argparse.ArgumentParser(description="Enforces common Android public API design \ 12561c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf patterns. It ignores lint messages from a previous API level, if provided.") 12571c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt") 12581c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None, 12591c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf help="previous.txt") 12601c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser.add_argument("--no-color", action='store_const', const=True, 12611c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf help="Disable terminal colors") 12621c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf parser.add_argument("--allow-google", action='store_const', const=True, 12631c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf help="Allow references to Google") 12641c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf args = vars(parser.parse_args()) 12651c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 12661c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if args['no_color']: 12671c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf USE_COLOR = False 12681c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 12691c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if args['allow_google']: 12701c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf ALLOW_GOOGLE = True 12711c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 12721c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf current_file = args['current.txt'] 12731c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf previous_file = args['previous.txt'] 12741c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf 12751c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf with current_file as f: 1276a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey cur_fail = examine_stream(f) 12771c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf if not previous_file is None: 12781c7e70aaf85576b6d63f98e19cc2356913e8cfacAdam Metcalf with previous_file as f: 1279a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey prev_fail = examine_stream(f) 12808190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1281ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey # ignore errors from previous API level 1282ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey for p in prev_fail: 1283ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey if p in cur_fail: 1284ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey del cur_fail[p] 12858190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1286a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """ 1287a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey # NOTE: disabled because of memory pressure 1288ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey # look for compatibility issues 1289ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey compat_fail = verify_compat(cur, prev) 1290037458a5bac2968eb0415c408d68c013d177ea3eJeff Sharkey 1291ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True))) 1292ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey for f in sorted(compat_fail): 1293ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print compat_fail[f] 1294ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print 1295a18a2e3428d73369be7ab1663792f0a080e037caJeff Sharkey """ 12968190f4885b3eb34231877003a583116a0e82826eJeff Sharkey 1297ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True))) 1298ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey for f in sorted(cur_fail): 1299ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print cur_fail[f] 1300ed6aaf0f79b7b39b2d70684c902d2d689a3db841Jeff Sharkey print 1301