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