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.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 equals(Object obj)125 public boolean equals(Object obj) { 126 if (this == obj) return true; 127 if (obj == null) return false; 128 if (getClass() != obj.getClass()) return false; 129 @SuppressWarnings("unchecked") 130 ApiVersion<T> other = (ApiVersion<T>) obj; 131 return (mMajorVersion == other.mMajorVersion) && (mMinorVersion == other.mMinorVersion); 132 } 133 134 /** 135 * @hide 136 */ 137 @Override hashCode()138 public int hashCode() { 139 int prime = 31; 140 int result = 1; 141 result = prime * result + mMajorVersion; 142 result = prime * result + mMinorVersion; 143 return result; 144 } 145 146 /** 147 * @hide 148 */ 149 @Override 150 @NonNull toString()151 public final String toString() { 152 StringBuilder builder = new StringBuilder(getClass().getSimpleName()).append('['); 153 if (!TextUtils.isEmpty(mVersionName)) { 154 builder.append("name=").append(mVersionName).append(", "); 155 } 156 return builder 157 .append("major=").append(mMajorVersion) 158 .append(", minor=").append(mMinorVersion) 159 .append(']').toString(); 160 } 161 162 /** 163 * @hide 164 */ 165 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 166 minPlatformVersion = PlatformVersion.TIRAMISU_0) writeToParcel(Parcel dest)167 protected void writeToParcel(Parcel dest) { 168 dest.writeString(mVersionName); 169 dest.writeInt(getMajorVersion()); 170 dest.writeInt(getMinorVersion()); 171 } 172 173 /** 174 * @hide 175 */ 176 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 177 minPlatformVersion = PlatformVersion.TIRAMISU_0) readFromParcel(Parcel source, ApiVersionFactory<T> factory)178 protected static <T extends ApiVersion<?>> T readFromParcel(Parcel source, 179 ApiVersionFactory<T> factory) { 180 String name = source.readString(); 181 int major = source.readInt(); 182 int minor = source.readInt(); 183 return factory.newInstance(name, major, minor); 184 } 185 186 /** 187 * @hide 188 */ 189 interface ApiVersionFactory<T extends ApiVersion<?>> { 190 @ApiRequirements(minCarVersion = CarVersion.TIRAMISU_1, 191 minPlatformVersion = PlatformVersion.TIRAMISU_0) newInstance(String name, int major, int minor)192 T newInstance(String name, int major, int minor); 193 } 194 } 195