1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2009 The Android Open Source Project 4*9e94795aSAndroid Build Coastguard Worker# 5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*9e94795aSAndroid Build Coastguard Worker# 11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Workerimport argparse 18*9e94795aSAndroid Build Coastguard Workerimport sys 19*9e94795aSAndroid Build Coastguard Worker 20*9e94795aSAndroid Build Coastguard Workerfrom uffd_gc_utils import should_enable_uffd_gc 21*9e94795aSAndroid Build Coastguard Worker 22*9e94795aSAndroid Build Coastguard Worker# Usage: post_process_props.py file.prop [disallowed_key, ...] 23*9e94795aSAndroid Build Coastguard Worker# Disallowed keys are removed from the property file, if present 24*9e94795aSAndroid Build Coastguard Worker 25*9e94795aSAndroid Build Coastguard Worker# See PROP_VALUE_MAX in system_properties.h. 26*9e94795aSAndroid Build Coastguard Worker# The constant in system_properties.h includes the terminating NUL, 27*9e94795aSAndroid Build Coastguard Worker# so we decrease the value by 1 here. 28*9e94795aSAndroid Build Coastguard WorkerPROP_VALUE_MAX = 91 29*9e94795aSAndroid Build Coastguard Worker 30*9e94795aSAndroid Build Coastguard Worker# Put the modifications that you need to make into the */build.prop into this 31*9e94795aSAndroid Build Coastguard Worker# function. 32*9e94795aSAndroid Build Coastguard Workerdef mangle_build_prop(prop_list, kernel_version_file_for_uffd_gc): 33*9e94795aSAndroid Build Coastguard Worker # If ro.debuggable is 1, then enable adb on USB by default 34*9e94795aSAndroid Build Coastguard Worker # (this is for userdebug builds) 35*9e94795aSAndroid Build Coastguard Worker if prop_list.get_value("ro.debuggable") == "1": 36*9e94795aSAndroid Build Coastguard Worker val = prop_list.get_value("persist.sys.usb.config") 37*9e94795aSAndroid Build Coastguard Worker if "adb" not in val: 38*9e94795aSAndroid Build Coastguard Worker if val == "": 39*9e94795aSAndroid Build Coastguard Worker val = "adb" 40*9e94795aSAndroid Build Coastguard Worker else: 41*9e94795aSAndroid Build Coastguard Worker val = val + ",adb" 42*9e94795aSAndroid Build Coastguard Worker prop_list.put("persist.sys.usb.config", val) 43*9e94795aSAndroid Build Coastguard Worker if prop_list.get_value("ro.dalvik.vm.enable_uffd_gc") == "default": 44*9e94795aSAndroid Build Coastguard Worker assert kernel_version_file_for_uffd_gc != "" 45*9e94795aSAndroid Build Coastguard Worker enable_uffd_gc = should_enable_uffd_gc(kernel_version_file_for_uffd_gc) 46*9e94795aSAndroid Build Coastguard Worker prop_list.put("ro.dalvik.vm.enable_uffd_gc", 47*9e94795aSAndroid Build Coastguard Worker "true" if enable_uffd_gc else "false") 48*9e94795aSAndroid Build Coastguard Worker 49*9e94795aSAndroid Build Coastguard Workerdef validate_grf_props(prop_list): 50*9e94795aSAndroid Build Coastguard Worker """Validate GRF properties if exist. 51*9e94795aSAndroid Build Coastguard Worker 52*9e94795aSAndroid Build Coastguard Worker If ro.board.first_api_level is defined, check if its value is valid. 53*9e94795aSAndroid Build Coastguard Worker 54*9e94795aSAndroid Build Coastguard Worker Returns: 55*9e94795aSAndroid Build Coastguard Worker True if the GRF properties are valid. 56*9e94795aSAndroid Build Coastguard Worker """ 57*9e94795aSAndroid Build Coastguard Worker grf_api_level = prop_list.get_value("ro.board.first_api_level") 58*9e94795aSAndroid Build Coastguard Worker board_api_level = prop_list.get_value("ro.board.api_level") 59*9e94795aSAndroid Build Coastguard Worker 60*9e94795aSAndroid Build Coastguard Worker if grf_api_level and board_api_level: 61*9e94795aSAndroid Build Coastguard Worker grf_api_level = int(grf_api_level) 62*9e94795aSAndroid Build Coastguard Worker board_api_level = int(board_api_level) 63*9e94795aSAndroid Build Coastguard Worker if board_api_level < grf_api_level: 64*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("error: ro.board.api_level(%d) must not be less than " 65*9e94795aSAndroid Build Coastguard Worker "ro.board.first_api_level(%d)\n" 66*9e94795aSAndroid Build Coastguard Worker % (board_api_level, grf_api_level)) 67*9e94795aSAndroid Build Coastguard Worker return False 68*9e94795aSAndroid Build Coastguard Worker 69*9e94795aSAndroid Build Coastguard Worker return True 70*9e94795aSAndroid Build Coastguard Worker 71*9e94795aSAndroid Build Coastguard Workerdef validate(prop_list): 72*9e94795aSAndroid Build Coastguard Worker """Validate the properties. 73*9e94795aSAndroid Build Coastguard Worker 74*9e94795aSAndroid Build Coastguard Worker If the value of a sysprop exceeds the max limit (91), it's an error, unless 75*9e94795aSAndroid Build Coastguard Worker the sysprop is a read-only one. 76*9e94795aSAndroid Build Coastguard Worker 77*9e94795aSAndroid Build Coastguard Worker Checks if there is no optional prop assignments. 78*9e94795aSAndroid Build Coastguard Worker 79*9e94795aSAndroid Build Coastguard Worker Returns: 80*9e94795aSAndroid Build Coastguard Worker True if nothing is wrong. 81*9e94795aSAndroid Build Coastguard Worker """ 82*9e94795aSAndroid Build Coastguard Worker check_pass = True 83*9e94795aSAndroid Build Coastguard Worker for p in prop_list.get_all_props(): 84*9e94795aSAndroid Build Coastguard Worker if len(p.value) > PROP_VALUE_MAX and not p.name.startswith("ro."): 85*9e94795aSAndroid Build Coastguard Worker check_pass = False 86*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("error: %s cannot exceed %d bytes: " % 87*9e94795aSAndroid Build Coastguard Worker (p.name, PROP_VALUE_MAX)) 88*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("%s (%d)\n" % (p.value, len(p.value))) 89*9e94795aSAndroid Build Coastguard Worker 90*9e94795aSAndroid Build Coastguard Worker if p.is_optional(): 91*9e94795aSAndroid Build Coastguard Worker check_pass = False 92*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("error: found unresolved optional prop assignment:\n") 93*9e94795aSAndroid Build Coastguard Worker sys.stderr.write(str(p) + "\n") 94*9e94795aSAndroid Build Coastguard Worker 95*9e94795aSAndroid Build Coastguard Worker return check_pass 96*9e94795aSAndroid Build Coastguard Worker 97*9e94795aSAndroid Build Coastguard Workerdef override_optional_props(prop_list, allow_dup=False): 98*9e94795aSAndroid Build Coastguard Worker """Override a?=b with a=c, if the latter exists 99*9e94795aSAndroid Build Coastguard Worker 100*9e94795aSAndroid Build Coastguard Worker Overriding is done by deleting a?=b 101*9e94795aSAndroid Build Coastguard Worker When there are a?=b and a?=c, then only the last one survives 102*9e94795aSAndroid Build Coastguard Worker When there are a=b and a=c, then it's an error. 103*9e94795aSAndroid Build Coastguard Worker 104*9e94795aSAndroid Build Coastguard Worker Returns: 105*9e94795aSAndroid Build Coastguard Worker True if the override was successful 106*9e94795aSAndroid Build Coastguard Worker """ 107*9e94795aSAndroid Build Coastguard Worker success = True 108*9e94795aSAndroid Build Coastguard Worker for name in prop_list.get_all_names(): 109*9e94795aSAndroid Build Coastguard Worker props = prop_list.get_props(name) 110*9e94795aSAndroid Build Coastguard Worker optional_props = [p for p in props if p.is_optional()] 111*9e94795aSAndroid Build Coastguard Worker overriding_props = [p for p in props if not p.is_optional()] 112*9e94795aSAndroid Build Coastguard Worker if len(overriding_props) > 1: 113*9e94795aSAndroid Build Coastguard Worker # duplicated props are allowed when the all have the same value 114*9e94795aSAndroid Build Coastguard Worker if all(overriding_props[0].value == p.value for p in overriding_props): 115*9e94795aSAndroid Build Coastguard Worker for p in optional_props: 116*9e94795aSAndroid Build Coastguard Worker p.delete("overridden by %s" % str(overriding_props[0])) 117*9e94795aSAndroid Build Coastguard Worker continue 118*9e94795aSAndroid Build Coastguard Worker # or if dup is explicitly allowed for compat reason 119*9e94795aSAndroid Build Coastguard Worker if allow_dup: 120*9e94795aSAndroid Build Coastguard Worker # this could left one or more optional props unresolved. 121*9e94795aSAndroid Build Coastguard Worker # Convert them into non-optional because init doesn't understand ?= 122*9e94795aSAndroid Build Coastguard Worker # syntax 123*9e94795aSAndroid Build Coastguard Worker for p in optional_props: 124*9e94795aSAndroid Build Coastguard Worker p.optional = False 125*9e94795aSAndroid Build Coastguard Worker continue 126*9e94795aSAndroid Build Coastguard Worker 127*9e94795aSAndroid Build Coastguard Worker success = False 128*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("error: found duplicate sysprop assignments:\n") 129*9e94795aSAndroid Build Coastguard Worker for p in overriding_props: 130*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("%s\n" % str(p)) 131*9e94795aSAndroid Build Coastguard Worker elif len(overriding_props) == 1: 132*9e94795aSAndroid Build Coastguard Worker for p in optional_props: 133*9e94795aSAndroid Build Coastguard Worker p.delete("overridden by %s" % str(overriding_props[0])) 134*9e94795aSAndroid Build Coastguard Worker else: 135*9e94795aSAndroid Build Coastguard Worker if len(optional_props) > 1: 136*9e94795aSAndroid Build Coastguard Worker for p in optional_props[:-1]: 137*9e94795aSAndroid Build Coastguard Worker p.delete("overridden by %s" % str(optional_props[-1])) 138*9e94795aSAndroid Build Coastguard Worker # Make the last optional one as non-optional 139*9e94795aSAndroid Build Coastguard Worker optional_props[-1].optional = False 140*9e94795aSAndroid Build Coastguard Worker 141*9e94795aSAndroid Build Coastguard Worker return success 142*9e94795aSAndroid Build Coastguard Worker 143*9e94795aSAndroid Build Coastguard Workerclass Prop: 144*9e94795aSAndroid Build Coastguard Worker 145*9e94795aSAndroid Build Coastguard Worker def __init__(self, name, value, optional=False, comment=None): 146*9e94795aSAndroid Build Coastguard Worker self.name = name.strip() 147*9e94795aSAndroid Build Coastguard Worker self.value = value.strip() 148*9e94795aSAndroid Build Coastguard Worker if comment != None: 149*9e94795aSAndroid Build Coastguard Worker self.comments = [comment] 150*9e94795aSAndroid Build Coastguard Worker else: 151*9e94795aSAndroid Build Coastguard Worker self.comments = [] 152*9e94795aSAndroid Build Coastguard Worker self.optional = optional 153*9e94795aSAndroid Build Coastguard Worker 154*9e94795aSAndroid Build Coastguard Worker @staticmethod 155*9e94795aSAndroid Build Coastguard Worker def from_line(line): 156*9e94795aSAndroid Build Coastguard Worker line = line.rstrip('\n') 157*9e94795aSAndroid Build Coastguard Worker if line.startswith("#"): 158*9e94795aSAndroid Build Coastguard Worker return Prop("", "", comment=line) 159*9e94795aSAndroid Build Coastguard Worker elif "?=" in line: 160*9e94795aSAndroid Build Coastguard Worker name, value = line.split("?=", 1) 161*9e94795aSAndroid Build Coastguard Worker return Prop(name, value, optional=True) 162*9e94795aSAndroid Build Coastguard Worker elif "=" in line: 163*9e94795aSAndroid Build Coastguard Worker name, value = line.split("=", 1) 164*9e94795aSAndroid Build Coastguard Worker return Prop(name, value, optional=False) 165*9e94795aSAndroid Build Coastguard Worker else: 166*9e94795aSAndroid Build Coastguard Worker # don't fail on invalid line 167*9e94795aSAndroid Build Coastguard Worker # TODO(jiyong) make this a hard error 168*9e94795aSAndroid Build Coastguard Worker return Prop("", "", comment=line) 169*9e94795aSAndroid Build Coastguard Worker 170*9e94795aSAndroid Build Coastguard Worker def is_comment(self): 171*9e94795aSAndroid Build Coastguard Worker return bool(self.comments and not self.name) 172*9e94795aSAndroid Build Coastguard Worker 173*9e94795aSAndroid Build Coastguard Worker def is_optional(self): 174*9e94795aSAndroid Build Coastguard Worker return (not self.is_comment()) and self.optional 175*9e94795aSAndroid Build Coastguard Worker 176*9e94795aSAndroid Build Coastguard Worker def make_as_comment(self): 177*9e94795aSAndroid Build Coastguard Worker # Prepend "#" to the last line which is the prop assignment 178*9e94795aSAndroid Build Coastguard Worker if not self.is_comment(): 179*9e94795aSAndroid Build Coastguard Worker assignment = str(self).rsplit("\n", 1)[-1] 180*9e94795aSAndroid Build Coastguard Worker self.comments.append("#" + assignment) 181*9e94795aSAndroid Build Coastguard Worker self.name = "" 182*9e94795aSAndroid Build Coastguard Worker self.value = "" 183*9e94795aSAndroid Build Coastguard Worker 184*9e94795aSAndroid Build Coastguard Worker def delete(self, reason): 185*9e94795aSAndroid Build Coastguard Worker self.comments.append("# Removed by post_process_props.py because " + reason) 186*9e94795aSAndroid Build Coastguard Worker self.make_as_comment() 187*9e94795aSAndroid Build Coastguard Worker 188*9e94795aSAndroid Build Coastguard Worker def __str__(self): 189*9e94795aSAndroid Build Coastguard Worker assignment = [] 190*9e94795aSAndroid Build Coastguard Worker if not self.is_comment(): 191*9e94795aSAndroid Build Coastguard Worker operator = "?=" if self.is_optional() else "=" 192*9e94795aSAndroid Build Coastguard Worker assignment.append(self.name + operator + self.value) 193*9e94795aSAndroid Build Coastguard Worker return "\n".join(self.comments + assignment) 194*9e94795aSAndroid Build Coastguard Worker 195*9e94795aSAndroid Build Coastguard Workerclass PropList: 196*9e94795aSAndroid Build Coastguard Worker 197*9e94795aSAndroid Build Coastguard Worker def __init__(self, filename): 198*9e94795aSAndroid Build Coastguard Worker with open(filename) as f: 199*9e94795aSAndroid Build Coastguard Worker self.props = [Prop.from_line(l) 200*9e94795aSAndroid Build Coastguard Worker for l in f.readlines() if l.strip() != ""] 201*9e94795aSAndroid Build Coastguard Worker 202*9e94795aSAndroid Build Coastguard Worker def get_all_props(self): 203*9e94795aSAndroid Build Coastguard Worker return [p for p in self.props if not p.is_comment()] 204*9e94795aSAndroid Build Coastguard Worker 205*9e94795aSAndroid Build Coastguard Worker def get_all_names(self): 206*9e94795aSAndroid Build Coastguard Worker return set([p.name for p in self.get_all_props()]) 207*9e94795aSAndroid Build Coastguard Worker 208*9e94795aSAndroid Build Coastguard Worker def get_props(self, name): 209*9e94795aSAndroid Build Coastguard Worker return [p for p in self.get_all_props() if p.name == name] 210*9e94795aSAndroid Build Coastguard Worker 211*9e94795aSAndroid Build Coastguard Worker def get_value(self, name): 212*9e94795aSAndroid Build Coastguard Worker # Caution: only the value of the first sysprop having the name is returned. 213*9e94795aSAndroid Build Coastguard Worker return next((p.value for p in self.props if p.name == name), "") 214*9e94795aSAndroid Build Coastguard Worker 215*9e94795aSAndroid Build Coastguard Worker def put(self, name, value): 216*9e94795aSAndroid Build Coastguard Worker # Note: when there is an optional prop for the name, its value isn't changed. 217*9e94795aSAndroid Build Coastguard Worker # Instead a new non-optional prop is appended, which will override the 218*9e94795aSAndroid Build Coastguard Worker # optional prop. Otherwise, the new value might be overridden by an existing 219*9e94795aSAndroid Build Coastguard Worker # non-optional prop of the same name. 220*9e94795aSAndroid Build Coastguard Worker index = next((i for i,p in enumerate(self.props) 221*9e94795aSAndroid Build Coastguard Worker if p.name == name and not p.is_optional()), -1) 222*9e94795aSAndroid Build Coastguard Worker if index == -1: 223*9e94795aSAndroid Build Coastguard Worker self.props.append(Prop(name, value, 224*9e94795aSAndroid Build Coastguard Worker comment="# Auto-added by post_process_props.py")) 225*9e94795aSAndroid Build Coastguard Worker else: 226*9e94795aSAndroid Build Coastguard Worker self.props[index].comments.append( 227*9e94795aSAndroid Build Coastguard Worker "# Value overridden by post_process_props.py. Original value: %s" % 228*9e94795aSAndroid Build Coastguard Worker self.props[index].value) 229*9e94795aSAndroid Build Coastguard Worker self.props[index].value = value 230*9e94795aSAndroid Build Coastguard Worker 231*9e94795aSAndroid Build Coastguard Worker def write(self, filename): 232*9e94795aSAndroid Build Coastguard Worker with open(filename, 'w+') as f: 233*9e94795aSAndroid Build Coastguard Worker for p in self.props: 234*9e94795aSAndroid Build Coastguard Worker f.write(str(p) + "\n") 235*9e94795aSAndroid Build Coastguard Worker 236*9e94795aSAndroid Build Coastguard Workerdef main(argv): 237*9e94795aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description="Post-process build.prop file") 238*9e94795aSAndroid Build Coastguard Worker parser.add_argument("--allow-dup", dest="allow_dup", action="store_true", 239*9e94795aSAndroid Build Coastguard Worker default=False) 240*9e94795aSAndroid Build Coastguard Worker parser.add_argument("filename") 241*9e94795aSAndroid Build Coastguard Worker parser.add_argument("disallowed_keys", metavar="KEY", type=str, nargs="*") 242*9e94795aSAndroid Build Coastguard Worker parser.add_argument("--sdk-version", type=int, required=True) 243*9e94795aSAndroid Build Coastguard Worker parser.add_argument("--kernel-version-file-for-uffd-gc", required=True) 244*9e94795aSAndroid Build Coastguard Worker args = parser.parse_args() 245*9e94795aSAndroid Build Coastguard Worker 246*9e94795aSAndroid Build Coastguard Worker if not args.filename.endswith("/build.prop"): 247*9e94795aSAndroid Build Coastguard Worker sys.stderr.write("bad command line: " + str(argv) + "\n") 248*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 249*9e94795aSAndroid Build Coastguard Worker 250*9e94795aSAndroid Build Coastguard Worker props = PropList(args.filename) 251*9e94795aSAndroid Build Coastguard Worker mangle_build_prop(props, args.kernel_version_file_for_uffd_gc) 252*9e94795aSAndroid Build Coastguard Worker if not override_optional_props(props, args.allow_dup): 253*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 254*9e94795aSAndroid Build Coastguard Worker if not validate_grf_props(props): 255*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 256*9e94795aSAndroid Build Coastguard Worker if not validate(props): 257*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 258*9e94795aSAndroid Build Coastguard Worker 259*9e94795aSAndroid Build Coastguard Worker # Drop any disallowed keys 260*9e94795aSAndroid Build Coastguard Worker for key in args.disallowed_keys: 261*9e94795aSAndroid Build Coastguard Worker for p in props.get_props(key): 262*9e94795aSAndroid Build Coastguard Worker p.delete("%s is a disallowed key" % key) 263*9e94795aSAndroid Build Coastguard Worker 264*9e94795aSAndroid Build Coastguard Worker props.write(args.filename) 265*9e94795aSAndroid Build Coastguard Worker 266*9e94795aSAndroid Build Coastguard Workerif __name__ == "__main__": 267*9e94795aSAndroid Build Coastguard Worker main(sys.argv) 268