1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.tool.apibuilder;
18 
19 import com.android.car.tool.data.ParsedData;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.charset.StandardCharsets;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 
31 public final class GenerateAPI {
32 
33     private static final boolean DBG = false;
34 
35     private static final String ANDROID_BUILD_TOP = "ANDROID_BUILD_TOP";
36     private static final String CAR_API_PATH =
37             "/packages/services/Car/car-lib/src/android/car";
38     private static final String CAR_SERVICE_PATH =
39             "/packages/services/Car/service/src";
40     private static final String CAR_BUILT_IN_API_PATH =
41             "/packages/services/Car/car-builtin-lib/src/android/car/builtin";
42     private static final String CSHS_PATH =
43             "/frameworks/opt/car/services/builtInServices/src";
44     private static final String CAR_API_ANNOTATION_TEST_FILE =
45             "/packages/services/Car/tests/carservice_unit_test/res/raw/car_api_classes.txt";
46     private static final String CAR_BUILT_IN_ANNOTATION_TEST_FILE =
47             "/packages/services/Car/tests/carservice_unit_test/res/raw/"
48             + "car_built_in_api_classes.txt";
49     private static final String CAR_HIDDEN_API_FILE =
50             "/packages/services/Car/tests/carservice_unit_test/res/raw/"
51             + "car_hidden_apis.txt";
52     private static final String CAR_ADDEDINORBEFORE_API_FILE =
53             "/packages/services/Car/tests/carservice_unit_test/res/raw/"
54                     + "car_addedinorbefore_apis.txt";
55     private static final String CSHS_NON_HIDDEN_CLASSES_FILE =
56             "/frameworks/opt/car/services/builtInServices/tests/res/raw/CSHS_classes.txt";
57 
58     private static final String PRINT_CLASSES = "--print-classes";
59     private static final String PRINT_NON_HIDDEN_CLASSES_CSHS = "--print-non-hidden-classes-CSHS";
60     private static final String UPDATE_CLASSES = "--update-classes";
61     private static final String UPDATE_NON_HIDDEN_CLASSES_CSHS = "--update-non-hidden-classes-CSHS";
62     private static final String PRINT_HIDDEN_APIS = "--print-hidden-apis";
63     private static final String PRINT_HIDDEN_APIS_WITH_CONSTR = "--print-hidden-apis-with-constr";
64     private static final String UPDATE_HIDDEN_APIS = "--update-hidden-apis";
65     private static final String PRINT_ALL_APIS = "--print-all-apis";
66     private static final String PRINT_ALL_APIS_WITH_CONSTR = "--print-all-apis-with-constr";
67     private static final String UPDATE_APIS_WITH_ADDEDINORBEFORE =
68             "--update-apis-with-addedinorbefore";
69     private static final String PLATFORM_VERSION_ASSERTION_CHECK =
70             "--platform-version-assertion-check";
71     private static final String PRINT_ALL_APIS_WITH_CAR_VERSION =
72             "--print-all-apis-with-car-version";
73     private static final String PRINT_INCORRECT_REQUIRES_API_USAGE_IN_CAR_SERVICE =
74             "--print-incorrect-requires-api-usage-in-car-service";
75     private static final String PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CAR_BUILT_IN =
76             "--print-addedin-without-requires-api-in-car-built-in";
77     private static final String PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CSHS =
78             "--print-addedin-without-requires-api-in-CSHS";
79     private static final String ROOT_DIR = "--root-dir";
80 
main(final String[] args)81     public static void main(final String[] args) throws Exception {
82         try {
83             if (args.length == 0) {
84                 printHelp();
85                 return;
86             }
87 
88             boolean runForCSHS = false;
89             boolean printClasses = false;
90             boolean printNonHiddenClassesCSHS = false;
91             boolean updateClasses = false;
92             boolean updateNonHiddenClassesCSHS = false;
93             boolean printHiddenApis = false;
94             boolean printHiddenApisWithConstr = false;
95             boolean updateHiddenApis = false;
96             boolean printAllApis = false;
97             boolean printAllApisWithConstr = false;
98             boolean updateApisWithAddedinorbefore = false;
99             boolean platformVersionCheck = false;
100             boolean printAllApisWithCarVersion = false;
101             boolean printIncorrectRequiresApiUsageInCarService = false;
102             boolean printAddedinWithoutRequiresApiInCarBuiltIn = false;
103             boolean printAddedinWithoutRequiresApiInCSHS = false;
104             String rootDir = System.getenv(ANDROID_BUILD_TOP);
105             // If print request is more than one. Use marker to separate data. This would be useful
106             // for executing multiple requests in one go.
107             int printRequests = 0;
108 
109             for (int i = 0; i < args.length; i++) {
110                 String arg = args[i];
111                 switch (arg) {
112                     case PRINT_CLASSES:
113                         printClasses = true;
114                         printRequests++;
115                         break;
116                     case PRINT_NON_HIDDEN_CLASSES_CSHS:
117                         printNonHiddenClassesCSHS = true;
118                         runForCSHS = true;
119                         printRequests++;
120                         break;
121                     case UPDATE_CLASSES:
122                         updateClasses = true;
123                         break;
124                     case UPDATE_NON_HIDDEN_CLASSES_CSHS:
125                         updateNonHiddenClassesCSHS = true;
126                         runForCSHS = true;
127                         break;
128                     case PRINT_HIDDEN_APIS:
129                         printHiddenApis = true;
130                         printRequests++;
131                         break;
132                     case PRINT_HIDDEN_APIS_WITH_CONSTR:
133                         printHiddenApisWithConstr = true;
134                         printRequests++;
135                         break;
136                     case UPDATE_HIDDEN_APIS:
137                         updateHiddenApis = true;
138                         break;
139                     case PRINT_ALL_APIS:
140                         printAllApis = true;
141                         printRequests++;
142                         break;
143                     case PRINT_ALL_APIS_WITH_CONSTR:
144                         printAllApisWithConstr = true;
145                         printRequests++;
146                         break;
147                     case UPDATE_APIS_WITH_ADDEDINORBEFORE:
148                         updateApisWithAddedinorbefore = true;
149                         break;
150                     case ROOT_DIR:
151                         rootDir = args[++i];
152                         break;
153                     case PLATFORM_VERSION_ASSERTION_CHECK:
154                         platformVersionCheck = true;
155                         break;
156                     case PRINT_ALL_APIS_WITH_CAR_VERSION:
157                         printAllApisWithCarVersion = true;
158                         break;
159                     case PRINT_INCORRECT_REQUIRES_API_USAGE_IN_CAR_SERVICE:
160                         printIncorrectRequiresApiUsageInCarService = true;
161                         break;
162                     case PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CAR_BUILT_IN:
163                         printAddedinWithoutRequiresApiInCarBuiltIn = true;
164                         break;
165                     case PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CSHS:
166                         printAddedinWithoutRequiresApiInCSHS = true;
167                         runForCSHS = true;
168                         printRequests++;
169                         break;
170                     default:
171                         System.out.println("Incorrect Arguments.");
172                         printHelp();
173                         return;
174                 }
175             }
176 
177             // rootDir must be set.
178             if (rootDir == null || rootDir.isEmpty()) {
179                 System.out.println("Root dir not set.");
180                 printHelp();
181                 return;
182             }
183 
184             // Do CarServiceHelperService related stuff here
185             if (runForCSHS) {
186                 List<File> allJavaFiles_CSHS = getAllFiles(new File(rootDir + CSHS_PATH));
187                 ParsedData parsedDataCSHS = new ParsedData();
188                 ParsedDataBuilder.populateParsedData(allJavaFiles_CSHS, parsedDataCSHS);
189                 if (printNonHiddenClassesCSHS) {
190                     printMarker(printRequests, PRINT_NON_HIDDEN_CLASSES_CSHS);
191                     print(ParsedDataHelper.getNonHiddenClassNamesOnly(parsedDataCSHS));
192                 }
193                 if (printAddedinWithoutRequiresApiInCSHS) {
194                     printMarker(printRequests, PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CSHS);
195                     print(ParsedDataHelper.getIncorrectRequiresApi(parsedDataCSHS));
196                 }
197                 if (updateNonHiddenClassesCSHS) {
198                     write(rootDir + CSHS_NON_HIDDEN_CLASSES_FILE,
199                             ParsedDataHelper.getNonHiddenClassNamesOnly(parsedDataCSHS));
200                 }
201                 return;
202             }
203 
204             List<File> allJavaFiles_carLib = getAllFiles(new File(rootDir + CAR_API_PATH));
205             List<File> allJavaFiles_carBuiltInLib = getAllFiles(
206                     new File(rootDir + CAR_BUILT_IN_API_PATH));
207 
208             ParsedData parsedDataCarLib = new ParsedData();
209             ParsedData parsedDataCarBuiltinLib = new ParsedData();
210             ParsedDataBuilder.populateParsedData(allJavaFiles_carLib, parsedDataCarLib);
211             ParsedDataBuilder.populateParsedData(allJavaFiles_carBuiltInLib,
212                     parsedDataCarBuiltinLib);
213 
214             if (printClasses) {
215                 printMarker(printRequests, PRINT_CLASSES);
216                 print(ParsedDataHelper.getClassNamesOnly(parsedDataCarLib));
217                 print(ParsedDataHelper.getClassNamesOnly(parsedDataCarBuiltinLib));
218             }
219 
220             if (updateClasses) {
221                 write(rootDir + CAR_API_ANNOTATION_TEST_FILE,
222                         ParsedDataHelper.getClassNamesOnly(parsedDataCarLib));
223                 write(rootDir + CAR_BUILT_IN_ANNOTATION_TEST_FILE,
224                         ParsedDataHelper.getClassNamesOnly(parsedDataCarBuiltinLib));
225             }
226 
227             if (updateHiddenApis) {
228                 write(rootDir + CAR_HIDDEN_API_FILE,
229                         ParsedDataHelper.getHiddenApisOnly(parsedDataCarLib));
230             }
231 
232             if (printHiddenApis) {
233                 printMarker(printRequests, PRINT_HIDDEN_APIS);
234                 print(ParsedDataHelper.getHiddenApisOnly(parsedDataCarLib));
235             }
236 
237             if (printHiddenApisWithConstr) {
238                 printMarker(printRequests, PRINT_HIDDEN_APIS_WITH_CONSTR);
239                 print(ParsedDataHelper.getHiddenApisWithHiddenConstructor(parsedDataCarLib));
240             }
241 
242             if (printAllApis) {
243                 printMarker(printRequests, PRINT_ALL_APIS);
244                 print(ParsedDataHelper.getAllApis(parsedDataCarLib));
245             }
246 
247             if (printAllApisWithConstr) {
248                 printMarker(printRequests, PRINT_ALL_APIS_WITH_CONSTR);
249                 print(ParsedDataHelper.getAllApisWithConstructor(parsedDataCarLib));
250             }
251 
252             if (updateApisWithAddedinorbefore) {
253                 write(rootDir + CAR_ADDEDINORBEFORE_API_FILE,
254                         ParsedDataHelper.getAddedInOrBeforeApisOnly(parsedDataCarLib));
255             }
256             if (platformVersionCheck) {
257                 printMarker(printRequests, PLATFORM_VERSION_ASSERTION_CHECK);
258                 print(ParsedDataHelper.checkAssertPlatformVersionAtLeast(parsedDataCarLib));
259             }
260             if (printAllApisWithCarVersion) {
261                 printMarker(printRequests, PRINT_ALL_APIS_WITH_CAR_VERSION);
262                 print(ParsedDataHelper.getApisWithVersion(parsedDataCarLib));
263                 print(ParsedDataHelper.getApisWithVersion(parsedDataCarBuiltinLib));
264             }
265             if (printIncorrectRequiresApiUsageInCarService) {
266                 printMarker(printRequests, PRINT_INCORRECT_REQUIRES_API_USAGE_IN_CAR_SERVICE);
267                 List<File> allJavaFiles_CarService =
268                         getAllFiles(new File(rootDir + CAR_SERVICE_PATH));
269                 ParsedData parsedDataCarService = new ParsedData();
270                 ParsedDataBuilder.populateParsedData(allJavaFiles_CarService, parsedDataCarService);
271                 print(ParsedDataHelper.getIncorrectRequiresApiUsage(parsedDataCarService));
272             }
273             if (printAddedinWithoutRequiresApiInCarBuiltIn) {
274                 printMarker(printRequests, PRINT_ADDEDIN_WITHOUT_REQUIRES_API_IN_CAR_BUILT_IN);
275                 print(ParsedDataHelper.getIncorrectRequiresApi(parsedDataCarBuiltinLib));
276             }
277         } catch (Exception e) {
278             throw e;
279         }
280     }
281 
printMarker(int printRequests, String request)282     private static void printMarker(int printRequests, String request) {
283         if (printRequests > 1) {
284             // Should not change this marker since it would break the other script.
285             System.out.println("Start-" + request);
286         }
287     }
288 
print(List<String> data)289     private static void print(List<String> data) {
290         for (String string : data) {
291             System.out.println(string);
292         }
293     }
294 
write(String filePath, List<String> data)295     private static void write(String filePath, List<String> data) throws IOException {
296         Collections.sort(data);
297         Path file = Paths.get(filePath);
298         Files.write(file, data, StandardCharsets.UTF_8);
299     }
300 
printHelp()301     private static void printHelp() {
302         System.out.println("**** Help ****");
303         System.out.println("At least one argument is required. Supported arguments - ");
304         System.out.println(PRINT_CLASSES + " prints the list of valid class and"
305                 + " interfaces.");
306         System.out.println(UPDATE_CLASSES + " updates the test file with the list"
307                 + " of valid class and interfaces. These files are updated"
308                 + " tests/carservice_unit_test/res/raw/car_api_classes.txt and"
309                 + " tests/carservice_unit_test/res/raw/car_built_in_api_classes.txt");
310         System.out.println(PRINT_HIDDEN_APIS + " prints hidden api list.");
311         System.out.println(PRINT_HIDDEN_APIS_WITH_CONSTR + " generates hidden api list with"
312                 + " hidden constructors.");
313         System.out.println(UPDATE_HIDDEN_APIS + " generates hidden api list. "
314                 + "Results would be updated in " + CAR_HIDDEN_API_FILE);
315         System.out.println(PRINT_ALL_APIS + " prints all apis");
316         System.out.println(PRINT_ALL_APIS + " prints all apis and constructors.");
317         System.out.println(UPDATE_APIS_WITH_ADDEDINORBEFORE
318                 + " generates the api list that contains the @AddedInOrBefore annotation. "
319                 + "Results would be updated in " + CAR_ADDEDINORBEFORE_API_FILE);
320         System.out.println(PLATFORM_VERSION_ASSERTION_CHECK
321                 + " : Iterates through APIs to ensure that APIs added after TIRAMISU_x have call "
322                 + "assertPlatformVersionAtLeast with the correct minPlatformVersion.");
323         System.out.println(PRINT_ALL_APIS_WITH_CAR_VERSION
324                 + " : Prints a list of all apis along with their min car version.");
325         System.out.println("Second argument is value of Git Root Directory. By default, "
326                 + "it is environment variable ANDROID_BUILD_TOP. If environment variable is not set"
327                 + "then provide using" + ROOT_DIR + " <directory>");
328     }
329 
getAllFiles(File folderName)330     private static List<File> getAllFiles(File folderName) {
331         List<File> allFiles = new ArrayList<>();
332         File[] files = folderName.listFiles();
333 
334         for (int i = 0; i < files.length; i++) {
335             if (files[i].isFile() && files[i].getName().endsWith(".java")) {
336                 if (DBG) {
337                     System.out.printf("File added %s\n", files[i]);
338                 }
339                 allFiles.add(files[i]);
340             }
341             if (files[i].isDirectory()) {
342                 allFiles.addAll(getAllFiles(files[i]));
343             }
344         }
345         // List files doesn't guarantee fixed order on all systems. It is better to sort the list.
346         Collections.sort(allFiles);
347         return allFiles;
348     }
349 }
350