xref: /aosp_15_r20/hardware/interfaces/automotive/vehicle/tools/translate_aidl_enums.py (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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