1 /* 2 * Copyright (C) 2023 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 package com.android.adservices.shared.testing; 17 18 import com.google.common.annotations.VisibleForTesting; 19 20 import java.util.Arrays; 21 import java.util.Collection; 22 import java.util.Objects; 23 24 /** Abstraction for Android SDK levels (so it can be used both on device and host side tests). */ 25 public final class AndroidSdk { 26 27 private static final Logger sLogger = new Logger(DynamicLogger.getInstance(), AndroidSdk.class); 28 29 /** Android version {@code RVC}. */ 30 public static final int RVC = 30; 31 32 /** Android version {@code SC}. */ 33 public static final int SC = 31; 34 35 /** Android version {@code SC_V2}. */ 36 public static final int SC_V2 = 32; 37 38 /** Android version {@code TM}. */ 39 public static final int TM = 33; 40 41 /** Android version {@code UC}. */ 42 public static final int UDC = 34; 43 44 /** Android version {@code VIC}. */ 45 public static final int VIC = 35; 46 47 /** Android version {@code BAK}. */ 48 public static final int BAK = 36; 49 50 /** Android version for unreleased builds}. */ 51 public static final int CUR_DEVELOPMENT = 10_000; // Build.CUR_DEVELOPMENT.CUR_DEVELOPMENT 52 53 /** 54 * Convenience for ranges that are "less than T" (for example {@code 55 * RequiresSdkRange(atMost=PRE_T)}), as S had 2 APIs (31 and 32) 56 */ 57 public static final int PRE_T = SC_V2; 58 59 // TODO(b/324919960): make it package-protected again or make sure it's unit tested. 60 /** Represents a specific SDK level. */ 61 public enum Level { 62 ANY(Integer.MIN_VALUE), 63 DEV(CUR_DEVELOPMENT), 64 R(RVC), 65 S(SC), 66 S2(SC_V2), 67 T(TM), 68 U(UDC), 69 V(VIC), 70 B(BAK); 71 72 private final int mLevel; 73 Level(int level)74 Level(int level) { 75 mLevel = level; 76 } 77 78 // TODO(b/324919960): make it package-protected again or make sure it's unit tested. 79 /** Checks if SDK is at least the given level. */ isAtLeast(Level level)80 public boolean isAtLeast(Level level) { 81 return mLevel >= level.mLevel; 82 } 83 84 // TODO(b/324919960): make it package-protected again or make sure it's unit tested. 85 /** Gets the numeric representation of the SDK level (like {@code 33}). */ getLevel()86 public int getLevel() { 87 return mLevel; 88 } 89 90 /** Gets the level abstraction for the given level). */ forLevel(int level)91 public static Level forLevel(int level) { 92 switch (level) { 93 case CUR_DEVELOPMENT: 94 return DEV; 95 case RVC: 96 return R; 97 case SC: 98 return S; 99 case SC_V2: 100 return S2; 101 case TM: 102 return T; 103 case UDC: 104 return U; 105 case VIC: 106 return V; 107 case BAK: 108 return B; 109 } 110 if (level > BAK) { 111 sLogger.e( 112 "WARNING: Level.forLevel() called with unsupported / unreleased level (%d);" 113 + " returning DEV (%d)", 114 level, DEV.mLevel); 115 return DEV; 116 } 117 throw new IllegalArgumentException("Unsupported level: " + level); 118 } 119 } 120 121 // TODO(b/324919960): make it package-protected again or make sure it's unit tested. 122 /** Represents a range of Android API levels. */ 123 public static final class Range { 124 // TODO(b/324919960): make them package-protected again or make sure it's unit tested. 125 public static final int NO_MIN = Integer.MIN_VALUE; 126 public static final int NO_MAX = Integer.MAX_VALUE; 127 128 private final int mMinLevel; 129 private final int mMaxLevel; 130 Range(int minLevel, int maxLevel)131 private Range(int minLevel, int maxLevel) { 132 if (minLevel > maxLevel || minLevel == NO_MAX || maxLevel == NO_MIN) { 133 throw new IllegalArgumentException( 134 "maxLevel (" 135 + maxLevel 136 + ") must equal or higher than minLevel (" 137 + minLevel 138 + ")"); 139 } 140 mMinLevel = minLevel; 141 mMaxLevel = maxLevel; 142 } 143 144 /** Gets a range without an upper boundary. */ forAtLeast(int level)145 public static Range forAtLeast(int level) { 146 return new Range(/* minLevel= */ level, NO_MAX); 147 } 148 149 /** Gets a range without a lower boundary. */ forAtMost(int level)150 public static Range forAtMost(int level) { 151 return new Range(NO_MIN, /* maxLevel= */ level); 152 } 153 154 /** Gets a range for the specific levels. */ forRange(int minLevel, int maxLevel)155 public static Range forRange(int minLevel, int maxLevel) { 156 return new Range(minLevel, maxLevel); 157 } 158 159 /** Gets a range for a specific level. */ forExactly(int level)160 public static Range forExactly(int level) { 161 return new Range(/* minLevel= */ level, /* maxLevel= */ level); 162 } 163 164 /** Gets a range that includes any level. */ forAnyLevel()165 public static Range forAnyLevel() { 166 return new Range(NO_MIN, NO_MAX); 167 } 168 169 /** Checks if the given level fits this range (inclusive). */ isInRange(int level)170 public boolean isInRange(int level) { 171 return level >= mMinLevel && level <= mMaxLevel; 172 } 173 174 @VisibleForTesting merge(Range... ranges)175 static Range merge(Range... ranges) { 176 return merge(Arrays.asList(ranges)); 177 } 178 merge(Collection<Range> ranges)179 static Range merge(Collection<Range> ranges) { 180 Objects.requireNonNull(ranges, "ranges cannot be null"); 181 if (ranges.isEmpty()) { 182 throw new IllegalArgumentException("ranges cannot be empty"); 183 } 184 int minRange = NO_MIN; 185 int maxRange = NO_MAX; 186 for (Range range : ranges) { 187 if (range == null) { 188 throw new IllegalArgumentException("ranges cannot have null range: " + ranges); 189 } 190 minRange = Math.max(minRange, range.mMinLevel); 191 maxRange = Math.min(maxRange, range.mMaxLevel); 192 } 193 return forRange(minRange, maxRange); 194 } 195 196 @Override hashCode()197 public int hashCode() { 198 return Objects.hash(mMaxLevel, mMinLevel); 199 } 200 201 @Override equals(Object obj)202 public boolean equals(Object obj) { 203 if (this == obj) return true; 204 if (obj == null) return false; 205 if (getClass() != obj.getClass()) return false; 206 Range other = (Range) obj; 207 return mMaxLevel == other.mMaxLevel && mMinLevel == other.mMinLevel; 208 } 209 210 @Override toString()211 public String toString() { 212 StringBuilder builder = new StringBuilder("AndroidSdkRange[minLevel="); 213 if (mMinLevel == NO_MIN) { 214 builder.append("OPEN"); 215 } else { 216 builder.append(mMinLevel); 217 } 218 builder.append(", maxLevel="); 219 if (mMaxLevel == NO_MAX) { 220 builder.append("OPEN"); 221 } else { 222 builder.append(mMaxLevel); 223 } 224 return builder.append(']').toString(); 225 } 226 } 227 AndroidSdk()228 private AndroidSdk() { 229 throw new UnsupportedOperationException(); 230 } 231 } 232