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