Morten Krogh-Jespersen | a28fcdb | 2022-01-28 09:48:00 +0100 | [diff] [blame] | 1 | // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | package com.android.tools.r8.androidapi; |
| 6 | |
| 7 | import com.android.tools.r8.graph.DexItemFactory; |
Morten Krogh-Jespersen | e742b1a | 2023-03-21 13:38:24 +0100 | [diff] [blame] | 8 | import com.android.tools.r8.graph.DexProto; |
Morten Krogh-Jespersen | a28fcdb | 2022-01-28 09:48:00 +0100 | [diff] [blame] | 9 | import com.android.tools.r8.graph.DexReference; |
Morten Krogh-Jespersen | b1bfc24 | 2022-01-31 14:56:52 +0100 | [diff] [blame] | 10 | import com.android.tools.r8.graph.DexType; |
Morten Krogh-Jespersen | a28fcdb | 2022-01-28 09:48:00 +0100 | [diff] [blame] | 11 | import com.android.tools.r8.utils.AndroidApiLevel; |
Morten Krogh-Jespersen | 7c44fe4 | 2023-05-09 10:27:26 +0200 | [diff] [blame] | 12 | import java.util.HashSet; |
| 13 | import java.util.Set; |
Morten Krogh-Jespersen | a28fcdb | 2022-01-28 09:48:00 +0100 | [diff] [blame] | 14 | import java.util.function.BiConsumer; |
| 15 | |
Morten Krogh-Jespersen | 7c44fe4 | 2023-05-09 10:27:26 +0200 | [diff] [blame] | 16 | public class AndroidApiLevelDatabaseHelper { |
| 17 | |
| 18 | public static Set<String> notModeledTypes() { |
Søren Gjesse | 7a36649 | 2024-06-26 09:38:58 +0200 | [diff] [blame] | 19 | // The types below are known not to be modeled by any api-versions. |
Morten Krogh-Jespersen | 7c44fe4 | 2023-05-09 10:27:26 +0200 | [diff] [blame] | 20 | Set<String> notModeledTypes = new HashSet<>(); |
| 21 | notModeledTypes.add("androidx.annotation.RecentlyNullable"); |
| 22 | notModeledTypes.add("androidx.annotation.RecentlyNonNull"); |
| 23 | notModeledTypes.add("android.annotation.Nullable"); |
| 24 | notModeledTypes.add("android.annotation.NonNull"); |
Søren Gjesse | 6fdc87c | 2024-06-19 12:43:22 +0200 | [diff] [blame] | 25 | notModeledTypes.add("android.annotation.FlaggedApi"); |
Søren Gjesse | 7a36649 | 2024-06-26 09:38:58 +0200 | [diff] [blame] | 26 | notModeledTypes.add( |
Søren Gjesse | 00f86ff | 2025-03-25 12:13:24 +0100 | [diff] [blame^] | 27 | "android.adservices.ondevicepersonalization.FederatedComputeScheduleRequest"); |
| 28 | notModeledTypes.add( |
| 29 | "android.adservices.ondevicepersonalization.FederatedComputeScheduleResponse"); |
Morten Krogh-Jespersen | 7c44fe4 | 2023-05-09 10:27:26 +0200 | [diff] [blame] | 30 | return notModeledTypes; |
| 31 | } |
Morten Krogh-Jespersen | a28fcdb | 2022-01-28 09:48:00 +0100 | [diff] [blame] | 32 | |
Søren Gjesse | 7a36649 | 2024-06-26 09:38:58 +0200 | [diff] [blame] | 33 | public static Set<String> notModeledFields() { |
| 34 | // The fields below are known not to be modeled by any api-versions. |
| 35 | Set<String> notModeledFields = new HashSet<>(); |
| 36 | notModeledFields.add("int android.app.appsearch.AppSearchResult.RESULT_DENIED"); |
| 37 | notModeledFields.add("int android.app.appsearch.AppSearchResult.RESULT_RATE_LIMITED"); |
Søren Gjesse | 00f86ff | 2025-03-25 12:13:24 +0100 | [diff] [blame^] | 38 | |
| 39 | notModeledFields.add( |
| 40 | "int android.adservices.adselection.ReportEventRequest.FLAG_REPORTING_DESTINATION_COMPONENT_SELLER"); |
| 41 | notModeledFields.add( |
| 42 | "int android.adservices.ondevicepersonalization.OnDevicePersonalizationManager.FEATURE_DISABLED"); |
| 43 | notModeledFields.add( |
| 44 | "int android.adservices.ondevicepersonalization.OnDevicePersonalizationManager.FEATURE_ENABLED"); |
| 45 | notModeledFields.add( |
| 46 | "int android.adservices.ondevicepersonalization.OnDevicePersonalizationManager.FEATURE_UNSUPPORTED"); |
| 47 | notModeledFields.add( |
| 48 | "int android.adservices.ondevicepersonalization.InferenceInput$Params.MODEL_TYPE_EXECUTORCH"); |
Søren Gjesse | 7a36649 | 2024-06-26 09:38:58 +0200 | [diff] [blame] | 49 | return notModeledFields; |
| 50 | } |
| 51 | |
| 52 | public static Set<String> notModeledMethods() { |
| 53 | // The methods below are known not to be modeled by any api-versions. |
| 54 | Set<String> notModelledMethods = new HashSet<>(); |
| 55 | notModelledMethods.add( |
| 56 | "void android.adservices.customaudience.CustomAudienceManager.scheduleCustomAudienceUpdate(android.adservices.customaudience.ScheduleCustomAudienceUpdateRequest," |
| 57 | + " java.util.concurrent.Executor," |
| 58 | + " android.adservices.common.AdServicesOutcomeReceiver)"); |
Søren Gjesse | 00f86ff | 2025-03-25 12:13:24 +0100 | [diff] [blame^] | 59 | notModelledMethods.add( |
| 60 | "android.adservices.ondevicepersonalization.InferenceOutput$Builder" |
| 61 | + " android.adservices.ondevicepersonalization.InferenceOutput$Builder.setData(byte[])"); |
| 62 | notModelledMethods.add( |
| 63 | "android.adservices.common.AdTechIdentifier" |
| 64 | + " android.adservices.adselection.AdSelectionOutcome.getWinningSeller()"); |
| 65 | notModelledMethods.add( |
| 66 | "void android.adservices.ondevicepersonalization.FederatedComputeScheduler.schedule(android.adservices.ondevicepersonalization.FederatedComputeScheduleRequest," |
| 67 | + " java.util.concurrent.Executor, android.os.OutcomeReceiver)"); |
| 68 | |
| 69 | notModelledMethods.add( |
| 70 | "byte[] android.adservices.ondevicepersonalization.InferenceOutput.getData()"); |
| 71 | notModelledMethods.add( |
| 72 | "byte[] android.adservices.ondevicepersonalization.InferenceInput.getData()"); |
| 73 | notModelledMethods.add( |
| 74 | "void android.adservices.ondevicepersonalization.OnDevicePersonalizationManager.queryFeatureAvailability(java.lang.String," |
| 75 | + " java.util.concurrent.Executor, android.os.OutcomeReceiver)"); |
| 76 | notModelledMethods.add( |
| 77 | "void android.adservices.ondevicepersonalization.InferenceInput$Builder.<init>(android.adservices.ondevicepersonalization.InferenceInput$Params," |
| 78 | + " byte[])"); |
| 79 | notModelledMethods.add( |
| 80 | "android.adservices.ondevicepersonalization.InferenceInput$Builder" |
| 81 | + " android.adservices.ondevicepersonalization.InferenceInput$Builder.setInputData(byte[])"); |
Søren Gjesse | 7a36649 | 2024-06-26 09:38:58 +0200 | [diff] [blame] | 82 | return notModelledMethods; |
| 83 | } |
| 84 | |
Morten Krogh-Jespersen | a28fcdb | 2022-01-28 09:48:00 +0100 | [diff] [blame] | 85 | static void visitAdditionalKnownApiReferences( |
| 86 | DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) { |
Morten Krogh-Jespersen | e742b1a | 2023-03-21 13:38:24 +0100 | [diff] [blame] | 87 | addStringBuilderAndBufferMethods(factory, apiLevelConsumer); |
| 88 | addConcurrentKeySetViewMethods(factory, apiLevelConsumer); |
| 89 | addNfcMethods(factory, apiLevelConsumer); |
| 90 | addWebkitCookieSyncManagerMethods(factory, apiLevelConsumer); |
| 91 | addChronoTimeMethods(factory, apiLevelConsumer); |
| 92 | } |
| 93 | |
| 94 | private static void addStringBuilderAndBufferMethods( |
| 95 | DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) { |
Morten Krogh-Jespersen | b1bfc24 | 2022-01-31 14:56:52 +0100 | [diff] [blame] | 96 | // StringBuilder and StringBuffer lack api definitions for the exact same methods in |
| 97 | // api-versions.xml. See b/216587554 for related error. |
| 98 | for (DexType type : new DexType[] {factory.stringBuilderType, factory.stringBufferType}) { |
| 99 | apiLevelConsumer.accept( |
| 100 | factory.createMethod(type, factory.createProto(factory.intType), "capacity"), |
| 101 | AndroidApiLevel.B); |
| 102 | apiLevelConsumer.accept( |
| 103 | factory.createMethod( |
| 104 | type, factory.createProto(factory.intType, factory.intType), "codePointAt"), |
| 105 | AndroidApiLevel.B); |
| 106 | apiLevelConsumer.accept( |
| 107 | factory.createMethod( |
| 108 | type, factory.createProto(factory.intType, factory.intType), "codePointBefore"), |
| 109 | AndroidApiLevel.B); |
| 110 | apiLevelConsumer.accept( |
| 111 | factory.createMethod( |
| 112 | type, |
| 113 | factory.createProto(factory.intType, factory.intType, factory.intType), |
| 114 | "codePointCount"), |
| 115 | AndroidApiLevel.B); |
| 116 | apiLevelConsumer.accept( |
| 117 | factory.createMethod( |
| 118 | type, factory.createProto(factory.voidType, factory.intType), "ensureCapacity"), |
| 119 | AndroidApiLevel.B); |
| 120 | apiLevelConsumer.accept( |
| 121 | factory.createMethod( |
| 122 | type, |
| 123 | factory.createProto( |
| 124 | factory.voidType, |
| 125 | factory.intType, |
| 126 | factory.intType, |
| 127 | factory.charArrayType, |
| 128 | factory.intType), |
| 129 | "getChars"), |
| 130 | AndroidApiLevel.B); |
| 131 | apiLevelConsumer.accept( |
| 132 | factory.createMethod( |
| 133 | type, factory.createProto(factory.intType, factory.stringType), "indexOf"), |
| 134 | AndroidApiLevel.B); |
| 135 | apiLevelConsumer.accept( |
| 136 | factory.createMethod( |
| 137 | type, |
| 138 | factory.createProto(factory.intType, factory.stringType, factory.intType), |
| 139 | "indexOf"), |
| 140 | AndroidApiLevel.B); |
| 141 | apiLevelConsumer.accept( |
| 142 | factory.createMethod( |
| 143 | type, factory.createProto(factory.intType, factory.stringType), "lastIndexOf"), |
| 144 | AndroidApiLevel.B); |
| 145 | apiLevelConsumer.accept( |
| 146 | factory.createMethod( |
| 147 | type, |
| 148 | factory.createProto(factory.intType, factory.stringType, factory.intType), |
| 149 | "lastIndexOf"), |
| 150 | AndroidApiLevel.B); |
| 151 | apiLevelConsumer.accept( |
| 152 | factory.createMethod( |
| 153 | type, |
| 154 | factory.createProto(factory.intType, factory.intType, factory.intType), |
| 155 | "offsetByCodePoints"), |
| 156 | AndroidApiLevel.B); |
| 157 | apiLevelConsumer.accept( |
| 158 | factory.createMethod( |
| 159 | type, |
| 160 | factory.createProto(factory.voidType, factory.intType, factory.charType), |
| 161 | "setCharAt"), |
| 162 | AndroidApiLevel.B); |
| 163 | apiLevelConsumer.accept( |
| 164 | factory.createMethod( |
| 165 | type, factory.createProto(factory.voidType, factory.intType), "setLength"), |
| 166 | AndroidApiLevel.B); |
| 167 | apiLevelConsumer.accept( |
| 168 | factory.createMethod( |
| 169 | type, factory.createProto(factory.stringType, factory.intType), "substring"), |
| 170 | AndroidApiLevel.B); |
| 171 | apiLevelConsumer.accept( |
| 172 | factory.createMethod( |
| 173 | type, |
| 174 | factory.createProto(factory.stringType, factory.intType, factory.intType), |
| 175 | "substring"), |
| 176 | AndroidApiLevel.B); |
| 177 | apiLevelConsumer.accept( |
| 178 | factory.createMethod(type, factory.createProto(factory.voidType), "trimToSize"), |
| 179 | AndroidApiLevel.B); |
| 180 | } |
Morten Krogh-Jespersen | a28fcdb | 2022-01-28 09:48:00 +0100 | [diff] [blame] | 181 | } |
Morten Krogh-Jespersen | e742b1a | 2023-03-21 13:38:24 +0100 | [diff] [blame] | 182 | |
| 183 | private static void addConcurrentKeySetViewMethods( |
| 184 | DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) { |
| 185 | // KeysetView.getMap was also added in N (24). |
| 186 | apiLevelConsumer.accept( |
| 187 | factory.createMethod( |
| 188 | factory.concurrentHashMapKeySetViewType, |
| 189 | factory.createProto(factory.concurrentHashMapType), |
| 190 | "getMap"), |
| 191 | AndroidApiLevel.N); |
| 192 | } |
| 193 | |
| 194 | private static void addNfcMethods( |
| 195 | DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) { |
| 196 | String[] nfcClasses = |
| 197 | new String[] { |
| 198 | "Landroid/nfc/tech/Ndef;", |
| 199 | "Landroid/nfc/tech/NfcA;", |
| 200 | "Landroid/nfc/tech/NfcB;", |
| 201 | "Landroid/nfc/tech/NfcBarcode;", |
| 202 | "Landroid/nfc/tech/NfcF;", |
| 203 | "Landroid/nfc/tech/NdefFormatable;", |
| 204 | "Landroid/nfc/tech/IsoDep;", |
| 205 | "Landroid/nfc/tech/MifareClassic;", |
| 206 | "Landroid/nfc/tech/MifareUltralight;", |
| 207 | "Landroid/nfc/tech/NfcV;" |
| 208 | }; |
| 209 | DexType tagType = factory.createType("Landroid/nfc/Tag;"); |
| 210 | // Seems like all methods are available from api level G_MR1 but we choose K since some of these |
| 211 | // classes are introduced at 17. |
| 212 | for (String nfcClass : nfcClasses) { |
| 213 | DexType nfcClassType = factory.createType(nfcClass); |
| 214 | apiLevelConsumer.accept( |
| 215 | factory.createMethod( |
| 216 | nfcClassType, factory.createProto(factory.booleanType), "isConnected"), |
| 217 | AndroidApiLevel.K); |
| 218 | apiLevelConsumer.accept( |
| 219 | factory.createMethod(nfcClassType, factory.createProto(tagType), "getTag"), |
| 220 | AndroidApiLevel.K); |
| 221 | apiLevelConsumer.accept( |
| 222 | factory.createMethod(nfcClassType, factory.createProto(factory.voidType), "close"), |
| 223 | AndroidApiLevel.K); |
| 224 | apiLevelConsumer.accept( |
| 225 | factory.createMethod(nfcClassType, factory.createProto(factory.voidType), "connect"), |
| 226 | AndroidApiLevel.K); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | private static void addWebkitCookieSyncManagerMethods( |
| 231 | DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) { |
| 232 | // All of these are added in android.jar from at least 14. |
| 233 | DexType cookieSyncManager = factory.createType("Landroid/webkit/CookieSyncManager;"); |
| 234 | DexProto voidProto = factory.createProto(factory.voidType); |
| 235 | for (String methodName : new String[] {"sync", "resetSync", "startSync", "stopSync", "run"}) { |
| 236 | apiLevelConsumer.accept( |
| 237 | factory.createMethod(cookieSyncManager, voidProto, methodName), AndroidApiLevel.I); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | private static void addChronoTimeMethods( |
| 242 | DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) { |
| 243 | DexType valueRangeType = factory.createType("Ljava/time/temporal/ValueRange;"); |
| 244 | DexType chronoLocalDateType = factory.createType("Ljava/time/chrono/ChronoLocalDate;"); |
| 245 | DexType temporalType = factory.createType("Ljava/time/temporal/Temporal;"); |
| 246 | DexType temporalFieldType = factory.createType("Ljava/time/temporal/TemporalField;"); |
| 247 | DexType temporalUnitType = factory.createType("Ljava/time/temporal/TemporalUnit;"); |
| 248 | DexType temporalAmountType = factory.createType("Ljava/time/temporal/TemporalAmount;"); |
| 249 | DexType temporalAdjusterType = factory.createType("Ljava/time/temporal/TemporalAdjuster;"); |
| 250 | |
| 251 | // All of these classes was added in 26. |
| 252 | String[] timeClasses = |
| 253 | new String[] { |
| 254 | "Ljava/time/chrono/JapaneseDate;", |
| 255 | "Ljava/time/chrono/MinguoDate;", |
| 256 | "Ljava/time/chrono/HijrahDate;", |
| 257 | "Ljava/time/chrono/ThaiBuddhistDate;" |
| 258 | }; |
| 259 | for (String timeClass : timeClasses) { |
| 260 | DexType timeType = factory.createType(timeClass); |
| 261 | // int lengthOfMonth() |
| 262 | apiLevelConsumer.accept( |
| 263 | factory.createMethod(timeType, factory.createProto(factory.intType), "lengthOfMonth"), |
| 264 | AndroidApiLevel.O); |
| 265 | // int lengthOfYear() |
| 266 | apiLevelConsumer.accept( |
| 267 | factory.createMethod(timeType, factory.createProto(factory.intType), "lengthOfYear"), |
| 268 | AndroidApiLevel.O); |
| 269 | // boolean isSupported(java.time.temporal.TemporalField) |
| 270 | apiLevelConsumer.accept( |
| 271 | factory.createMethod( |
| 272 | timeType, factory.createProto(factory.booleanType, temporalFieldType), "isSupported"), |
| 273 | AndroidApiLevel.O); |
| 274 | // java.time.temporal.ValueRange range(java.time.temporal.TemporalField) |
| 275 | apiLevelConsumer.accept( |
| 276 | factory.createMethod( |
| 277 | timeType, factory.createProto(valueRangeType, temporalFieldType), "range"), |
| 278 | AndroidApiLevel.O); |
| 279 | // long getLong(java.time.temporal.TemporalField) |
| 280 | apiLevelConsumer.accept( |
| 281 | factory.createMethod( |
| 282 | timeType, factory.createProto(factory.longType, temporalFieldType), "getLong"), |
| 283 | AndroidApiLevel.O); |
| 284 | // java.time.chrono.ChronoLocalDateTime atTime(java.time.LocalTime) |
| 285 | apiLevelConsumer.accept( |
| 286 | factory.createMethod( |
| 287 | timeType, |
| 288 | factory.createProto( |
| 289 | factory.createType("Ljava/time/chrono/ChronoLocalDateTime;"), |
| 290 | factory.createType("Ljava/time/LocalTime;")), |
| 291 | "atTime"), |
| 292 | AndroidApiLevel.O); |
| 293 | // java.time.chrono.ChronoPeriod |
| 294 | // java.time.chrono.JapaneseDate.until(java.time.chrono.ChronoLocalDate) |
| 295 | apiLevelConsumer.accept( |
| 296 | factory.createMethod( |
| 297 | timeType, |
| 298 | factory.createProto( |
| 299 | factory.createType("Ljava/time/chrono/ChronoPeriod;"), chronoLocalDateType), |
| 300 | "until"), |
| 301 | AndroidApiLevel.O); |
| 302 | // long toEpochDay() |
| 303 | apiLevelConsumer.accept( |
| 304 | factory.createMethod(timeType, factory.createProto(factory.longType), "toEpochDay"), |
| 305 | AndroidApiLevel.O); |
| 306 | // long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit) |
| 307 | apiLevelConsumer.accept( |
| 308 | factory.createMethod( |
| 309 | timeType, |
| 310 | factory.createProto(factory.longType, temporalType, temporalUnitType), |
| 311 | "until"), |
| 312 | AndroidApiLevel.O); |
| 313 | |
| 314 | // java.time.chrono.Era getEra() |
| 315 | apiLevelConsumer.accept( |
| 316 | factory.createMethod( |
| 317 | timeType, |
| 318 | factory.createProto(factory.createType("Ljava/time/chrono/Era;")), |
| 319 | "getEra"), |
| 320 | AndroidApiLevel.O); |
| 321 | // java.time.chrono.Chronology getChronology() |
| 322 | apiLevelConsumer.accept( |
| 323 | factory.createMethod( |
| 324 | timeType, |
| 325 | factory.createProto(factory.createType("Ljava/time/chrono/Chronology;")), |
| 326 | "getChronology"), |
| 327 | AndroidApiLevel.O); |
| 328 | DexType[] returnTypesForModificationMethods = |
| 329 | new DexType[] {chronoLocalDateType, temporalType}; |
| 330 | for (DexType returnType : returnTypesForModificationMethods) { |
| 331 | // [returnType] minus(long, java.time.temporal.TemporalUnit) |
| 332 | apiLevelConsumer.accept( |
| 333 | factory.createMethod( |
| 334 | timeType, |
| 335 | factory.createProto(returnType, factory.longType, temporalUnitType), |
| 336 | "minus"), |
| 337 | AndroidApiLevel.O); |
| 338 | // [returnType] minus(java.time.temporal.TemporalAmount) |
| 339 | apiLevelConsumer.accept( |
| 340 | factory.createMethod( |
| 341 | timeType, factory.createProto(returnType, temporalAmountType), "minus"), |
| 342 | AndroidApiLevel.O); |
| 343 | // [returnType] plus(long, java.time.temporal.TemporalUnit) |
| 344 | apiLevelConsumer.accept( |
| 345 | factory.createMethod( |
| 346 | timeType, |
| 347 | factory.createProto(returnType, factory.longType, temporalUnitType), |
| 348 | "plus"), |
| 349 | AndroidApiLevel.O); |
| 350 | // [returnType] plus(java.time.temporal.TemporalAmount) |
| 351 | apiLevelConsumer.accept( |
| 352 | factory.createMethod( |
| 353 | timeType, factory.createProto(returnType, temporalAmountType), "plus"), |
| 354 | AndroidApiLevel.O); |
| 355 | // [returnType] with(java.time.temporal.TemporalField, long) |
| 356 | apiLevelConsumer.accept( |
| 357 | factory.createMethod( |
| 358 | timeType, |
| 359 | factory.createProto(returnType, temporalFieldType, factory.longType), |
| 360 | "with"), |
| 361 | AndroidApiLevel.O); |
| 362 | // [returnType] with(java.time.temporal.TemporalAdjuster) |
| 363 | apiLevelConsumer.accept( |
| 364 | factory.createMethod( |
| 365 | timeType, factory.createProto(returnType, temporalAdjusterType), "with"), |
| 366 | AndroidApiLevel.O); |
| 367 | } |
| 368 | } |
| 369 | // boolean java.time.chrono.HijrahDate.isLeapYear() |
| 370 | apiLevelConsumer.accept( |
| 371 | factory.createMethod( |
| 372 | factory.createType("Ljava/time/chrono/HijrahDate;"), |
| 373 | factory.createProto(factory.booleanType), |
| 374 | "isLeapYear"), |
| 375 | AndroidApiLevel.O); |
| 376 | } |
Morten Krogh-Jespersen | a28fcdb | 2022-01-28 09:48:00 +0100 | [diff] [blame] | 377 | } |