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