1#!/usr/bin/python3 2 3# Copyright (C) 2023 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17"""A script to generate ENUM_NAME.java file and test files using ENUM_NAME.aidl file. 18 19 Need ANDROID_BUILD_TOP environmental variable to be set. This script will update ENUM_NAME.java 20 under packages/services/Car/car-lib/src/android/car/hardware/property, as well as the 21 ENUM_NAMETest.java files in cts/tests/tests/car/src/android/car/cts and 22 packages/services/Car/tests/android_car_api_test/src/android/car/apitest 23 24 Also needs a flag name e.g. FLAG_ANDROID_VIC_VEHICLE_PROPERTIES 25 26 Usage: 27 $ python translate_aidl_enums.py ENUM_NAME.aidl FLAG_NAME 28""" 29import os 30import sys 31 32LICENSE = """/* 33 * Copyright (C) 2024 The Android Open Source Project 34 * 35 * Licensed under the Apache License, Version 2.0 (the "License"); 36 * you may not use this file except in compliance with the License. 37 * You may obtain a copy of the License at 38 * 39 * http://www.apache.org/licenses/LICENSE-2.0 40 * 41 * Unless required by applicable law or agreed to in writing, software 42 * distributed under the License is distributed on an "AS IS" BASIS, 43 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 44 * See the License for the specific language governing permissions and 45 * limitations under the License. 46 */ 47""" 48 49class EnumParser: 50 def __init__(self, file_path, file_name, flag_name): 51 self.filePath = file_path 52 self.fileName = file_name 53 self.flagName = flag_name 54 self.lowerFileName = self.fileName[0].lower() + self.fileName[1:] 55 self.enumNames = [] 56 self.enums = [] 57 self.outputMsg = [] 58 self.outputMsg.append(LICENSE) 59 self.outputMsg.append("\npackage android.car.hardware.property;\n") 60 self.outputMsg.append(""" 61import static android.car.feature.Flags.{}; 62 63import android.annotation.FlaggedApi; 64import android.annotation.IntDef; 65import android.annotation.NonNull; 66 67import com.android.car.internal.util.ConstantDebugUtils; 68 69import java.lang.annotation.Retention; 70import java.lang.annotation.RetentionPolicy; 71""".format(self.flagName)) 72 73 comment_block = [] 74 in_comment = False 75 76 with open(self.filePath, 'r') as f: 77 lines = f.readlines() 78 for line in lines: 79 line = line.rstrip('\n') 80 if line.strip() in ["package android.hardware.automotive.vehicle;", 81 "@VintfStability", 82 '@Backing(type="int")']: 83 continue 84 85 if line.strip().startswith('/**') or line.strip().startswith('/*'): 86 in_comment = True 87 comment_block.append(line + '\n') 88 continue 89 elif in_comment: 90 comment_block.append(line + '\n') 91 if line.strip().endswith('*/'): 92 in_comment = False 93 continue 94 elif line.strip().startswith('*'): 95 comment_block.append(line + '\n') 96 continue 97 98 msg = line + '\n' 99 msgSplit = msg.strip().split() 100 if len(msgSplit) > 0 and msgSplit[0] == "enum": 101 if comment_block: 102 self.outputMsg.extend(comment_block) 103 comment_block = [] 104 self.outputMsg.append("@FlaggedApi({})\n".format(self.flagName)) 105 msgSplit[0] = "public final class" 106 msg = " ".join(msgSplit) + "\n" 107 self.outputMsg.append(msg) 108 elif len(msgSplit) > 1 and msgSplit[1] == '=': 109 if comment_block: 110 indented_comment_block = [line for line in comment_block] 111 self.outputMsg.extend(indented_comment_block) 112 comment_block = [] 113 msgSplit.insert(0, " public static final int") 114 enum_name = msgSplit[1].strip() 115 self.enumNames.append(enum_name) 116 enum = msgSplit[3].strip(",") 117 self.enums.append(enum) 118 if msgSplit[-1].endswith(','): 119 msgSplit[-1] = msgSplit[-1][:-1] + ";" 120 msg = " ".join(msgSplit) + "\n" 121 self.outputMsg.append(msg) 122 elif line.strip() == '}': 123 if comment_block: 124 self.outputMsg.extend(comment_block) 125 comment_block = [] 126 self.outputMsg.append(""" 127 private {2}() {{}} 128 129 /** 130 * Returns a user-friendly representation of {{@code {2}}}. 131 */ 132 @NonNull 133 public static String toString(@{2}Int int {0}) {{ 134 String {0}String = ConstantDebugUtils.toName( 135 {2}.class, {0}); 136 return ({0}String != null) 137 ? {0}String 138 : "0x" + Integer.toHexString({0}); 139 }} 140 141 /** @hide */ 142 @IntDef({1}) 143 @Retention(RetentionPolicy.SOURCE) 144 public @interface {2}Int {{}}\n""".format(self.lowerFileName, "{" + ", ".join(self.enums) + "}", 145 self.fileName)) 146 self.outputMsg.append("}") 147 148 self.outputMsgApiTest = [] 149 self.outputMsgApiTest.append(LICENSE) 150 self.outputMsgApiTest.append("""package android.car.apitest; 151 152import static android.car.feature.Flags.{1}; 153 154import static com.google.common.truth.Truth.assertWithMessage; 155 156import android.platform.test.annotations.RequiresFlagsEnabled; 157import android.platform.test.flag.junit.CheckFlagsRule; 158import android.platform.test.flag.junit.DeviceFlagsValueProvider; 159 160import androidx.test.filters.SmallTest; 161 162import org.junit.Rule; 163import org.junit.Test; 164import org.junit.runner.RunWith; 165import org.junit.runners.Parameterized; 166 167import java.util.Arrays; 168import java.util.Collection; 169 170@SmallTest 171@RunWith(Parameterized.class) 172public class {0}Test {{ 173 @Rule 174 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 175 private final int mJavaConstantValue; 176 private final int mHalConstantValue; 177 178 public {0}Test(int javaConstantValue, int halConstantValue) {{ 179 mJavaConstantValue = javaConstantValue; 180 mHalConstantValue = halConstantValue; 181 }} 182 183 @Parameterized.Parameters 184 public static Collection constantValues() {{ 185 return Arrays.asList( 186 new Object[][] {{""".format(self.fileName, self.flagName)) 187 for enum in self.enumNames: 188 self.outputMsgApiTest.append(""" 189 {{ 190 android.car.hardware.property.{0}.{1}, 191 android.hardware.automotive.vehicle.{0}.{1} 192 }},""".format(self.fileName, enum)) 193 self.outputMsgApiTest.append(""" 194 }}); 195 }} 196 197 @Test 198 @RequiresFlagsEnabled({}) 199 public void testMatchWithVehicleHal() {{ 200 assertWithMessage("Java constant") 201 .that(mJavaConstantValue) 202 .isEqualTo(mHalConstantValue); 203 }} 204}} 205""".format(self.flagName)) 206 207 self.outputMsgCtsTest = [] 208 self.outputMsgCtsTest.append(LICENSE) 209 self.outputMsgCtsTest.append(""" 210package android.car.cts; 211 212import static android.car.feature.Flags.{1}; 213 214import static com.google.common.truth.Truth.assertThat; 215import static com.google.common.truth.Truth.assertWithMessage; 216 217import android.car.cts.utils.VehiclePropertyUtils; 218import android.car.hardware.property.{0}; 219import android.platform.test.annotations.RequiresFlagsEnabled; 220import android.platform.test.flag.junit.CheckFlagsRule; 221import android.platform.test.flag.junit.DeviceFlagsValueProvider; 222 223import org.junit.Rule; 224import org.junit.Test; 225 226import java.util.List; 227 228public class {0}Test {{ 229 @Rule 230 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 231 232 @Test 233 @RequiresFlagsEnabled({1}) 234 public void testToString() {{""".format(self.fileName, self.flagName)) 235 for enum in self.enumNames: 236 self.outputMsgCtsTest.append(""" 237 assertThat({0}.toString( 238 {0}.{1})) 239 .isEqualTo("{1}");""".format(self.fileName, enum)) 240 max_enum_value = len(self.enums) 241 self.outputMsgCtsTest.append(""" 242 assertThat({0}.toString({1})).isEqualTo("{2}"); 243 assertThat({0}.toString(12)).isEqualTo("0xc"); 244 }} 245 246 @Test 247 @RequiresFlagsEnabled({4}) 248 public void testAll{0}sAreMappedInToString() {{ 249 List<Integer> {3}s = 250 VehiclePropertyUtils.getIntegersFromDataEnums({0}.class); 251 for (Integer {3} : {3}s) {{ 252 String {3}String = {0}.toString( 253 {3}); 254 assertWithMessage("%s starts with 0x", {3}String).that( 255 {3}String.startsWith("0x")).isFalse(); 256 }} 257 }} 258}} 259""".format(self.fileName, len(self.enums), hex(len(self.enums)), self.lowerFileName, self.flagName)) 260 261def main(): 262 if len(sys.argv) != 3: 263 print("Usage: {} enum_aidl_file ALL_CAPS_FLAG_NAME".format(sys.argv[0])) 264 sys.exit(1) 265 print("WARNING: This file only generates the base enum values in the framework layer. The " 266 + "generated files must be reviewed by you and edited if any additional changes are " 267 + "required. The java enum file should be updated with app-developer facing " 268 + "documentation, the @FlaggedApi tag for the new API, and with the @SystemApi tag if " 269 + "the new property is system API") 270 file_path = sys.argv[1] 271 file_name = file_path.split('/')[-1][:-5] 272 flag_name = sys.argv[2] 273 parser = EnumParser(file_path, file_name, flag_name) 274 275 android_top = os.environ['ANDROID_BUILD_TOP'] 276 if not android_top: 277 print('ANDROID_BUILD_TOP is not in environmental variable, please run source and lunch ' 278 + 'at the android root') 279 sys.exit(1) 280 281 with open(android_top + "/packages/services/Car/car-lib/src/android/car/hardware/property/" 282 + file_name + ".java", 'w') as f: 283 f.write("".join(parser.outputMsg)) 284 285 with open(android_top 286 + "/packages/services/Car/tests/android_car_api_test/src/android/car/apitest/" 287 + file_name + "Test.java", 'w') as f: 288 f.write("".join(parser.outputMsgApiTest)) 289 290 with open(android_top + "/cts/tests/tests/car/src/android/car/cts/" + file_name + "Test.java", 291 'w') as f: 292 f.write("".join(parser.outputMsgCtsTest)) 293 294if __name__ == "__main__": 295 main() 296