1 /* 2 * Copyright (C) 2016 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 androidx.core.os 18 19 import android.os.Build 20 import android.os.ext.SdkExtensions 21 import androidx.annotation.ChecksSdkIntAtLeast 22 import androidx.annotation.RequiresApi 23 import androidx.annotation.RestrictTo 24 import androidx.annotation.VisibleForTesting 25 26 /** 27 * This class contains additional platform version checking methods for targeting pre-release 28 * versions of Android. 29 */ 30 public object BuildCompat { 31 32 /** 33 * Checks if the codename is a matching or higher version than the given build value. 34 * 35 * @param codename the requested build codename, e.g. `"O"` or `"OMR1"` 36 * @param buildCodename the value of [Build.VERSION.CODENAME] 37 * @return `true` if APIs from the requested codename are available in the build. 38 */ 39 @JvmStatic 40 @RestrictTo(RestrictTo.Scope.LIBRARY) 41 @VisibleForTesting isAtLeastPreReleaseCodenamenull42 public fun isAtLeastPreReleaseCodename(codename: String, buildCodename: String): Boolean { 43 fun codenameToInt(codename: String): Int? = 44 when (codename.uppercase()) { 45 "BAKLAVA" -> 0 46 else -> null 47 } 48 49 // Special case "REL", which means the build is not a pre-release build. 50 if ("REL" == buildCodename) { 51 return false 52 } 53 54 // Starting with Baklava, the Android dessert names wrapped around to the start of the 55 // alphabet; handle these "new" codenames explicitly; lexically compare "old" codenames. 56 // Return true if the build codename is equal to or greater than the requested codename. 57 val buildCodenameInt = codenameToInt(buildCodename) 58 val codenameInt = codenameToInt(codename) 59 if (buildCodenameInt != null && codenameInt != null) { 60 // both codenames are "new" -> use hard-coded int values 61 return buildCodenameInt >= codenameInt 62 } else if (buildCodenameInt == null && codenameInt == null) { 63 // both codenames are "old" -> use lexical comparison 64 return buildCodename.uppercase() >= codename.uppercase() 65 } else { 66 // one codename is "new", one is "old" 67 return buildCodenameInt != null 68 } 69 } 70 71 /** 72 * Checks if the device is running on the Android N release or newer. 73 * 74 * @return `true` if N APIs are available for use 75 */ 76 @JvmStatic 77 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) 78 @Deprecated( 79 message = 80 "Android N is a finalized release and this method is no longer necessary. " + 81 "It will be removed in a future release of this library. Instead, use " + 82 "`Build.VERSION.SDK_INT >= 24`.", 83 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 24") 84 ) isAtLeastNnull85 public fun isAtLeastN(): Boolean = Build.VERSION.SDK_INT >= 24 86 87 /** 88 * Checks if the device is running on the Android N MR1 release or newer. 89 * 90 * @return `true` if N MR1 APIs are available for use 91 */ 92 @JvmStatic 93 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N_MR1) 94 @Deprecated( 95 message = 96 "Android N MR1 is a finalized release and this method is no longer necessary. " + 97 "It will be removed in a future release of this library. Instead, use " + 98 "`Build.VERSION.SDK_INT >= 25`.", 99 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 25") 100 ) 101 public fun isAtLeastNMR1(): Boolean = Build.VERSION.SDK_INT >= 25 102 103 /** 104 * Checks if the device is running on a release version of Android O or newer. 105 * 106 * @return `true` if O APIs are available for use, `false` otherwise 107 */ 108 @JvmStatic 109 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) 110 @Deprecated( 111 message = 112 "Android O is a finalized release and this method is no longer necessary. " + 113 "It will be removed in a future release of this library. Instead use " + 114 "`Build.VERSION.SDK_INT >= 26`.", 115 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 26") 116 ) 117 public fun isAtLeastO(): Boolean = Build.VERSION.SDK_INT >= 26 118 119 /** 120 * Checks if the device is running on a release version of Android O MR1 or newer. 121 * 122 * @return `true` if O MR1 APIs are available for use, `false` otherwise 123 */ 124 @JvmStatic 125 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O_MR1) 126 @Deprecated( 127 message = 128 "Android O MR1 is a finalized release and this method is no longer necessary. " + 129 "It will be removed in a future release of this library. Instead, use " + 130 "`Build.VERSION.SDK_INT >= 27`.", 131 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 27") 132 ) 133 public fun isAtLeastOMR1(): Boolean = Build.VERSION.SDK_INT >= 27 134 135 /** 136 * Checks if the device is running on a release version of Android P or newer. 137 * 138 * @return `true` if P APIs are available for use, `false` otherwise 139 */ 140 @JvmStatic 141 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) 142 @Deprecated( 143 message = 144 "Android P is a finalized release and this method is no longer necessary. " + 145 "It will be removed in a future release of this library. Instead, use " + 146 "`Build.VERSION.SDK_INT >= 28`.", 147 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 28") 148 ) 149 public fun isAtLeastP(): Boolean = Build.VERSION.SDK_INT >= 28 150 151 /** 152 * Checks if the device is running on release version of Android Q or newer. 153 * 154 * @return `true` if Q APIs are available for use, `false` otherwise 155 */ 156 @JvmStatic 157 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q) 158 @Deprecated( 159 message = 160 "Android Q is a finalized release and this method is no longer necessary. " + 161 "It will be removed in a future release of this library. Instead, use " + 162 "`Build.VERSION.SDK_INT >= 29`.", 163 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 29") 164 ) 165 public fun isAtLeastQ(): Boolean = Build.VERSION.SDK_INT >= 29 166 167 /** 168 * Checks if the device is running on release version of Android R or newer. 169 * 170 * @return `true` if R APIs are available for use, `false` otherwise 171 */ 172 @JvmStatic 173 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) 174 @Deprecated( 175 message = 176 "Android R is a finalized release and this method is no longer necessary. " + 177 "It will be removed in a future release of this library. Instead, use " + 178 "`Build.VERSION.SDK_INT >= 30`.", 179 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 30") 180 ) 181 public fun isAtLeastR(): Boolean = Build.VERSION.SDK_INT >= 30 182 183 /** 184 * Checks if the device is running on a pre-release version of Android S or a release version of 185 * Android S or newer. 186 * 187 * @return `true` if S APIs are available for use, `false` otherwise 188 */ 189 @JvmStatic 190 @ChecksSdkIntAtLeast(api = 31, codename = "S") 191 @Deprecated( 192 message = 193 "Android S is a finalized release and this method is no longer necessary. " + 194 "It will be removed in a future release of this library. Instead, use " + 195 "`Build.VERSION.SDK_INT >= 31`.", 196 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 31") 197 ) 198 public fun isAtLeastS(): Boolean = 199 Build.VERSION.SDK_INT >= 31 || 200 (Build.VERSION.SDK_INT >= 30 && 201 isAtLeastPreReleaseCodename("S", Build.VERSION.CODENAME)) 202 203 /** 204 * Checks if the device is running on a pre-release version of Android Sv2 or a release version 205 * of Android Sv2 or newer. 206 * 207 * @return `true` if Sv2 APIs are available for use, `false` otherwise 208 */ 209 @JvmStatic 210 @ChecksSdkIntAtLeast(api = 32, codename = "Sv2") 211 @Deprecated( 212 message = 213 "Android Sv2 is a finalized release and this method is no longer necessary. " + 214 "It will be removed in a future release of this library. Instead, use " + 215 "`Build.VERSION.SDK_INT >= 32`.", 216 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 32") 217 ) 218 public fun isAtLeastSv2(): Boolean = 219 Build.VERSION.SDK_INT >= 32 || 220 (Build.VERSION.SDK_INT >= 31 && 221 isAtLeastPreReleaseCodename("Sv2", Build.VERSION.CODENAME)) 222 223 /** 224 * Checks if the device is running on a pre-release version of Android Tiramisu or a release 225 * version of Android Tiramisu or newer. 226 * 227 * **Note:** When Android Tiramisu is finalized for release, this method will be removed and all 228 * calls must be replaced with `Build.VERSION.SDK_INT >= 33`. 229 * 230 * @return `true` if Tiramisu APIs are available for use, `false` otherwise 231 */ 232 @JvmStatic 233 @ChecksSdkIntAtLeast(api = 33, codename = "Tiramisu") 234 @Deprecated( 235 message = 236 "Android Tiramisu is a finalized release and this method is no longer " + 237 "necessary. It will be removed in a future release of this library. Instead, use " + 238 "`Build.VERSION.SDK_INT >= 33`.", 239 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 33") 240 ) 241 public fun isAtLeastT(): Boolean = 242 Build.VERSION.SDK_INT >= 33 || 243 (Build.VERSION.SDK_INT >= 32 && 244 isAtLeastPreReleaseCodename("Tiramisu", Build.VERSION.CODENAME)) 245 246 /** 247 * Checks if the device is running on a pre-release version of Android UpsideDownCake or a 248 * release version of Android UpsideDownCake or newer. 249 * 250 * **Note:** When Android UpsideDownCake is finalized for release, this method will be removed 251 * and all calls must be replaced with `Build.VERSION.SDK_INT >= 34`. 252 * 253 * @return `true` if UpsideDownCake APIs are available for use, `false` otherwise 254 */ 255 @JvmStatic 256 @ChecksSdkIntAtLeast(api = 34, codename = "UpsideDownCake") 257 @Deprecated( 258 message = 259 "Android UpsideDownCase is a finalized release and this method is no longer " + 260 "necessary. It will be removed in a future release of this library. Instead, use " + 261 "`Build.VERSION.SDK_INT >= 34`.", 262 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 34") 263 ) 264 public fun isAtLeastU(): Boolean = 265 Build.VERSION.SDK_INT >= 34 || 266 (Build.VERSION.SDK_INT >= 33 && 267 isAtLeastPreReleaseCodename("UpsideDownCake", Build.VERSION.CODENAME)) 268 269 /** 270 * Checks if the device is running on a pre-release version of Android VanillaIceCream or a 271 * release version of Android VanillaIceCream or newer. 272 * 273 * **Note:** When Android VanillaIceCream is finalized for release, this method will be removed 274 * and all calls must be replaced with `Build.VERSION.SDK_INT >= 35`. 275 * 276 * @return `true` if VanillaIceCream APIs are available for use, `false` otherwise 277 */ 278 @JvmStatic 279 @ChecksSdkIntAtLeast(api = 35, codename = "VanillaIceCream") 280 @Deprecated( 281 message = 282 "Android VanillaIceCream is a finalized release and this method is no longer " + 283 "necessary. It will be removed in a future release of this library. Instead, use " + 284 "`Build.VERSION.SDK_INT >= 35`.", 285 ReplaceWith("android.os.Build.VERSION.SDK_INT >= 35") 286 ) 287 public fun isAtLeastV(): Boolean = 288 Build.VERSION.SDK_INT >= 35 || 289 (Build.VERSION.SDK_INT >= 34 && 290 isAtLeastPreReleaseCodename("VanillaIceCream", Build.VERSION.CODENAME)) 291 292 /** 293 * Checks if the device is running on a pre-release version of Android Baklava or a release 294 * version of Android Baklava or newer. 295 * 296 * **Note:** When Android Baklava is finalized for release, this method will be removed and all 297 * calls must be replaced with `Build.VERSION.SDK_INT >= 36`. 298 * 299 * @return `true` if Baklava APIs are available for use, `false` otherwise 300 */ 301 @JvmStatic 302 @ChecksSdkIntAtLeast(api = 36, codename = "Baklava") 303 public fun isAtLeastB(): Boolean = 304 Build.VERSION.SDK_INT >= 36 || 305 (Build.VERSION.SDK_INT >= 35 && 306 isAtLeastPreReleaseCodename("Baklava", Build.VERSION.CODENAME)) 307 308 /** 309 * Experimental feature set for pre-release SDK checks. 310 * 311 * Pre-release SDK checks **do not** guarantee correctness, as APIs may have been added or 312 * removed during the course of a pre-release SDK development cycle. 313 * 314 * Additionally, pre-release checks **may not** return `true` when run on a finalized version of 315 * the SDK associated with the codename. 316 */ 317 @RequiresOptIn 318 @Retention(AnnotationRetention.BINARY) 319 public annotation class PrereleaseSdkCheck 320 321 /** 322 * The value of `SdkExtensions.getExtensionVersion(R)`. This is a convenience constant which 323 * provides the extension version in a similar style to `Build.VERSION.SDK_INT`. 324 * 325 * Compared to calling `getExtensionVersion` directly, using this constant has the benefit of 326 * not having to verify the `getExtensionVersion` method is available. 327 * 328 * @return the version of the R extension, if it exists. 0 otherwise. 329 */ 330 @JvmField 331 @ChecksSdkIntAtLeast(extension = Build.VERSION_CODES.R) 332 public val R_EXTENSION_INT: Int = 333 if (Build.VERSION.SDK_INT >= 30) { 334 Api30Impl.getExtensionVersion(Build.VERSION_CODES.R) 335 } else 0 336 337 /** 338 * The value of `SdkExtensions.getExtensionVersion(S)`. This is a convenience constant which 339 * provides the extension version in a similar style to `Build.VERSION.SDK_INT`. 340 * 341 * Compared to calling `getExtensionVersion` directly, using this constant has the benefit of 342 * not having to verify the `getExtensionVersion` method is available. 343 * 344 * @return the version of the S extension, if it exists. 0 otherwise. 345 */ 346 @JvmField 347 @ChecksSdkIntAtLeast(extension = Build.VERSION_CODES.S) 348 public val S_EXTENSION_INT: Int = 349 if (Build.VERSION.SDK_INT >= 30) { 350 Api30Impl.getExtensionVersion(Build.VERSION_CODES.S) 351 } else 0 352 353 /** 354 * The value of `SdkExtensions.getExtensionVersion(TIRAMISU)`. This is a convenience constant 355 * which provides the extension version in a similar style to `Build.VERSION.SDK_INT`. 356 * 357 * Compared to calling `getExtensionVersion` directly, using this constant has the benefit of 358 * not having to verify the `getExtensionVersion` method is available. 359 * 360 * @return the version of the T extension, if it exists. 0 otherwise. 361 */ 362 @JvmField 363 @ChecksSdkIntAtLeast(extension = Build.VERSION_CODES.TIRAMISU) 364 public val T_EXTENSION_INT: Int = 365 if (Build.VERSION.SDK_INT >= 30) { 366 Api30Impl.getExtensionVersion(Build.VERSION_CODES.TIRAMISU) 367 } else 0 368 369 /** 370 * The value of `SdkExtensions.getExtensionVersion(AD_SERVICES)`. This is a convenience constant 371 * which provides the extension version in a similar style to `Build.VERSION.SDK_INT`. 372 * 373 * Compared to calling `getExtensionVersion` directly, using this constant has the benefit of 374 * not having to verify the `getExtensionVersion` method is available. 375 * 376 * @return the version of the AdServices extension, if it exists. 0 otherwise. 377 */ 378 @JvmField 379 @ChecksSdkIntAtLeast(extension = SdkExtensions.AD_SERVICES) 380 public val AD_SERVICES_EXTENSION_INT: Int = 381 if (Build.VERSION.SDK_INT >= 30) { 382 Api30Impl.getExtensionVersion(SdkExtensions.AD_SERVICES) 383 } else 0 384 385 @RequiresApi(30) 386 private object Api30Impl { 387 getExtensionVersionnull388 fun getExtensionVersion(extension: Int): Int { 389 return SdkExtensions.getExtensionVersion(extension) 390 } 391 } 392 } 393