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