1 /* 2 * Copyright (C) 2022 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 android.car; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.car.annotation.ApiRequirements; 21 import android.car.annotation.ApiRequirements.CarVersion; 22 import android.car.annotation.ApiRequirements.PlatformVersion; 23 import android.os.Parcel; 24 import android.text.TextUtils; 25 26 import java.util.Objects; 27 28 /** 29 * Abstraction of Android APIs. 30 * 31 * <p>This class is used to represent a pair of major / minor API versions: the "major" version 32 * represents a "traditional" Android SDK release, while the "minor" is used to indicate incremental 33 * releases for that major. 34 * 35 * <p>This class is needed because the standard Android SDK API versioning only supports major 36 * releases, but {@code Car} APIs can now (starting on 37 * {@link android.os.Build.Build.VERSION_CODES#TIRAMISU Android 13}) be updated on minor releases 38 * as well. 39 * 40 * @param <T> implementation type 41 */ 42 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 43 minPlatformVersion = PlatformVersion.TIRAMISU_0) 44 public abstract class ApiVersion<T extends ApiVersion<?>> { 45 46 /** 47 * When set, it's used on {@link #toString()} - useful for versions that are pre-defined 48 * (like {@code TIRAMISU_1}). 49 */ 50 @Nullable 51 private final String mVersionName; 52 53 private final int mMajorVersion; 54 private final int mMinorVersion; 55 ApiVersion(int majorVersion, int minorVersion)56 ApiVersion(int majorVersion, int minorVersion) { 57 this(/* name= */ null, majorVersion, minorVersion); 58 } 59 ApiVersion(String name, int majorVersion, int minorVersion)60 ApiVersion(String name, int majorVersion, int minorVersion) { 61 mVersionName = name; 62 mMajorVersion = majorVersion; 63 mMinorVersion = minorVersion; 64 } 65 66 /** 67 * Checks if this API version meets the required version. 68 * 69 * @param requiredApiVersionMajor Required major version number. 70 * @param requiredApiVersionMinor Required minor version number. 71 * @return {@code true} if the {@link #getMajorVersion() major version} is newer than the 72 * {@code requiredVersion}'s major or if the {@link #getMajorVersion() major version} is 73 * the same as {@code requiredVersion}'s major with the {@link #getMinorVersion() minor 74 * version} the same or newer than {@code requiredVersion}'s minor. 75 * @throws IllegalArgumentException if {@code requiredVersion} is not an instance of the same 76 * class as this object. 77 */ 78 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 79 minPlatformVersion = PlatformVersion.TIRAMISU_0) isAtLeast(@onNull T requiredVersion)80 public final boolean isAtLeast(@NonNull T requiredVersion) { 81 Objects.requireNonNull(requiredVersion); 82 83 if (!this.getClass().isInstance(requiredVersion)) { 84 throw new IllegalArgumentException("Cannot compare " + this.getClass().getName() 85 + " against " + requiredVersion.getClass().getName()); 86 } 87 88 int requiredApiVersionMajor = requiredVersion.getMajorVersion(); 89 int requiredApiVersionMinor = requiredVersion.getMinorVersion(); 90 91 return (mMajorVersion > requiredApiVersionMajor) 92 || (mMajorVersion == requiredApiVersionMajor 93 && mMinorVersion >= requiredApiVersionMinor); 94 } 95 96 /** 97 * Gets the major version of the API represented by this object. 98 */ 99 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 100 minPlatformVersion = PlatformVersion.TIRAMISU_0) getMajorVersion()101 public final int getMajorVersion() { 102 return mMajorVersion; 103 } 104 105 /** 106 * Gets the minor version change of API for the same {@link #getMajorVersion()}. 107 * 108 * <p>It will reset to {@code 0} whenever {@link #getMajorVersion()} is updated 109 * and will increase by {@code 1} if car builtin or other car platform part is changed with the 110 * same {@link #getMajorVersion()}. 111 * 112 * <p>Client should check this version to use APIs which were added in a minor-only version 113 * update. 114 */ 115 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 116 minPlatformVersion = PlatformVersion.TIRAMISU_0) getMinorVersion()117 public final int getMinorVersion() { 118 return mMinorVersion; 119 } 120 121 /** 122 * @hide 123 */ 124 @Override 125 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 126 minPlatformVersion = PlatformVersion.TIRAMISU_0) equals(Object obj)127 public boolean equals(Object obj) { 128 if (this == obj) return true; 129 if (obj == null) return false; 130 if (getClass() != obj.getClass()) return false; 131 @SuppressWarnings("unchecked") 132 ApiVersion<T> other = (ApiVersion<T>) obj; 133 if (mMajorVersion != other.mMajorVersion) return false; 134 if (mMinorVersion != other.mMinorVersion) return false; 135 return true; 136 } 137 138 /** 139 * @hide 140 */ 141 @Override 142 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 143 minPlatformVersion = PlatformVersion.TIRAMISU_0) hashCode()144 public int hashCode() { 145 int prime = 31; 146 int result = 1; 147 result = prime * result + mMajorVersion; 148 result = prime * result + mMinorVersion; 149 return result; 150 } 151 152 /** 153 * @hide 154 */ 155 @Override 156 @NonNull 157 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 158 minPlatformVersion = PlatformVersion.TIRAMISU_0) toString()159 public final String toString() { 160 StringBuilder builder = new StringBuilder(getClass().getSimpleName()).append('['); 161 if (!TextUtils.isEmpty(mVersionName)) { 162 builder.append("name=").append(mVersionName).append(", "); 163 } 164 return builder 165 .append("major=").append(mMajorVersion) 166 .append(", minor=").append(mMinorVersion) 167 .append(']').toString(); 168 } 169 170 /** 171 * @hide 172 */ 173 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 174 minPlatformVersion = PlatformVersion.TIRAMISU_0) writeToParcel(Parcel dest)175 protected void writeToParcel(Parcel dest) { 176 dest.writeString(mVersionName); 177 dest.writeInt(getMajorVersion()); 178 dest.writeInt(getMinorVersion()); 179 } 180 181 /** 182 * @hide 183 */ 184 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 185 minPlatformVersion = PlatformVersion.TIRAMISU_0) readFromParcel(Parcel source, ApiVersionFactory<T> factory)186 protected static <T extends ApiVersion<?>> T readFromParcel(Parcel source, 187 ApiVersionFactory<T> factory) { 188 String name = source.readString(); 189 int major = source.readInt(); 190 int minor = source.readInt(); 191 return factory.newInstance(name, major, minor); 192 } 193 194 /** 195 * @hide 196 */ 197 interface ApiVersionFactory<T extends ApiVersion<?>> { 198 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 199 minPlatformVersion = PlatformVersion.TIRAMISU_0) newInstance(String name, int major, int minor)200 T newInstance(String name, int major, int minor); 201 } 202 } 203