1 /* 2 * Copyright (C) 2021 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.bedstead.nene.utils; 18 19 import static android.os.Build.VERSION.CODENAME; 20 21 import android.os.Build; 22 import android.os.Build.VERSION; 23 import android.util.Log; 24 25 import com.google.common.collect.ImmutableSet; 26 27 import java.lang.reflect.Field; 28 29 /** SDK Version checks. */ 30 public final class Versions { 31 32 private static final String TAG = "Versions"; 33 34 public static final int R = Build.VERSION_CODES.R; 35 public static final int T = Build.VERSION_CODES.TIRAMISU; 36 public static final int U = Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 37 public static final int V = Build.VERSION_CODES.VANILLA_ICE_CREAM; 38 public static final int W = Build.VERSION_CODES.CUR_DEVELOPMENT; 39 40 /** Any version. */ 41 public static final int ANY = -1; 42 43 private static final ImmutableSet<String> DEVELOPMENT_CODENAMES = 44 ImmutableSet.of("UpsideDownCake", "VanillaIceCream"); 45 Versions()46 private Versions() { 47 48 } 49 50 /** 51 * Throw a {@link UnsupportedOperationException} if the minimum version requirement is not met. 52 */ requireMinimumVersion(int min)53 public static void requireMinimumVersion(int min) { 54 if (!meetsSdkVersionRequirements(min, ANY)) { 55 String currentVersion = meetsMinimumSdkVersionRequirement(R) 56 ? Build.VERSION.RELEASE_OR_CODENAME : Integer.toString(Build.VERSION.SDK_INT); 57 throw new UnsupportedOperationException( 58 "This feature is only available on " 59 + versionToLetter(min) 60 + "+ (currently " + currentVersion + ")"); 61 } 62 } 63 versionToLetter(int version)64 private static String versionToLetter(int version) { 65 for (Field field : Build.VERSION_CODES.class.getFields()) { 66 if (!java.lang.reflect.Modifier.isStatic(field.getModifiers())) { 67 continue; 68 } 69 if (!field.getType().equals(int.class)) { 70 continue; 71 } 72 try { 73 int fieldValue = (int) field.get(null); 74 75 if (fieldValue == version) { 76 return field.getName(); 77 } 78 } catch (IllegalAccessException e) { 79 // Couldn't access this variable - ignore 80 } 81 } 82 83 return Integer.toString(version); 84 } 85 86 /** 87 * {@code true} if the minimum version requirement is met. 88 */ meetsMinimumSdkVersionRequirement(int min)89 public static boolean meetsMinimumSdkVersionRequirement(int min) { 90 return meetsSdkVersionRequirements(min, ANY); 91 } 92 93 /** 94 * {@code true} if the minimum and maximum version requirements are met. 95 * 96 * <p>Use {@link #ANY} to accept any version. 97 */ meetsSdkVersionRequirements(int min, int max)98 public static boolean meetsSdkVersionRequirements(int min, int max) { 99 if (min != ANY) { 100 if (min == Build.VERSION_CODES.CUR_DEVELOPMENT) { 101 if (!DEVELOPMENT_CODENAMES.contains(CODENAME)) { 102 Log.e(TAG, "meetsSdkVersionRequirements(" + min + "," + max 103 + "): false1 (Current: " + CODENAME + ", sdk: " 104 + VERSION.SDK_INT + ")"); 105 return false; 106 } 107 } else if (min > Build.VERSION.SDK_INT) { 108 Log.e(TAG, "meetsSdkVersionRequirements(" + min + "," 109 + max + "): false2 (Current: " + CODENAME + ", sdk: " 110 + VERSION.SDK_INT + ")"); 111 return false; 112 } 113 } 114 115 if (max != ANY && max != Integer.MAX_VALUE 116 && max != Build.VERSION_CODES.CUR_DEVELOPMENT) { 117 if (max < Build.VERSION.SDK_INT) { 118 Log.e(TAG, "meetsSdkVersionRequirements(" + min + "," 119 + max + "): false3 (Current: " + CODENAME + ", sdk: " 120 + VERSION.SDK_INT + ")"); 121 return false; 122 } 123 if (DEVELOPMENT_CODENAMES.contains(CODENAME)) { 124 Log.e(TAG, "meetsSdkVersionRequirements(" + min + "," 125 + max + "): false4 (Current: " + CODENAME + ", sdk: " 126 + VERSION.SDK_INT + ")"); 127 return false; 128 } 129 } 130 131 Log.e(TAG, "meetsSdkVersionRequirements(" + min + "," + max 132 + "): true (Current: " + CODENAME + ", sdk: " + VERSION.SDK_INT + ")"); 133 return true; 134 } 135 136 /** 137 * {@code true} if the current running version is the latest in-development version. 138 */ isDevelopmentVersion()139 public static boolean isDevelopmentVersion() { 140 return Build.VERSION.SDK_INT == Build.VERSION_CODES.CUR_DEVELOPMENT 141 && DEVELOPMENT_CODENAMES.contains(CODENAME); 142 } 143 } 144