/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.car; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.text.TextUtils; import java.util.Objects; /** * Abstraction of Android APIs. * *

This class is used to represent a pair of major / minor API versions: the "major" version * represents a "traditional" Android SDK release, while the "minor" is used to indicate incremental * releases for that major. * *

This class is needed because the standard Android SDK API versioning only supports major * releases, but {@code Car} APIs can now (starting on * {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13}) be updated on minor releases * as well. * * @param implementation type */ public abstract class ApiVersion> { /** * When set, it's used on {@link #toString()} - useful for versions that are pre-defined * (like {@code TIRAMISU_1}). */ @Nullable private final String mVersionName; private final int mMajorVersion; private final int mMinorVersion; ApiVersion(int majorVersion, int minorVersion) { this(/* name= */ null, majorVersion, minorVersion); } ApiVersion(String name, int majorVersion, int minorVersion) { mVersionName = name; mMajorVersion = majorVersion; mMinorVersion = minorVersion; } /** * Checks if this API version meets the required version. * * @param requiredVersion required major and minor version number * @return {@code true} if the {@link #getMajorVersion() major version} is newer than the * {@code requiredVersion}'s major or if the {@link #getMajorVersion() major version} is * the same as {@code requiredVersion}'s major with the {@link #getMinorVersion() minor * version} the same or newer than {@code requiredVersion}'s minor * @throws IllegalArgumentException if {@code requiredVersion} is not an instance of the same * class as this object */ public final boolean isAtLeast(@NonNull T requiredVersion) { Objects.requireNonNull(requiredVersion); if (!this.getClass().isInstance(requiredVersion)) { throw new IllegalArgumentException("Cannot compare " + this.getClass().getName() + " against " + requiredVersion.getClass().getName()); } int requiredApiVersionMajor = requiredVersion.getMajorVersion(); int requiredApiVersionMinor = requiredVersion.getMinorVersion(); return (mMajorVersion > requiredApiVersionMajor) || (mMajorVersion == requiredApiVersionMajor && mMinorVersion >= requiredApiVersionMinor); } /** * Gets the major version of the API represented by this object. */ public final int getMajorVersion() { return mMajorVersion; } /** * Gets the minor version change of API for the same {@link #getMajorVersion()}. * *

It will reset to {@code 0} whenever {@link #getMajorVersion()} is updated * and will increase by {@code 1} if car builtin or other car platform part is changed with the * same {@link #getMajorVersion()}. * *

Client should check this version to use APIs which were added in a minor-only version * update. */ public final int getMinorVersion() { return mMinorVersion; } /** * @hide */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; @SuppressWarnings("unchecked") ApiVersion other = (ApiVersion) obj; return (mMajorVersion == other.mMajorVersion) && (mMinorVersion == other.mMinorVersion); } /** * @hide */ @Override public int hashCode() { int prime = 31; int result = 1; result = prime * result + mMajorVersion; result = prime * result + mMinorVersion; return result; } /** * @hide */ @Override @NonNull public final String toString() { StringBuilder builder = new StringBuilder(getClass().getSimpleName()).append('['); if (!TextUtils.isEmpty(mVersionName)) { builder.append("name=").append(mVersionName).append(", "); } return builder .append("major=").append(mMajorVersion) .append(", minor=").append(mMinorVersion) .append(']').toString(); } /** * @hide */ protected void writeToParcel(Parcel dest) { dest.writeString(mVersionName); dest.writeInt(getMajorVersion()); dest.writeInt(getMinorVersion()); } /** * @hide */ protected static > T readFromParcel(Parcel source, ApiVersionFactory factory) { String name = source.readString(); int major = source.readInt(); int minor = source.readInt(); return factory.newInstance(name, major, minor); } /** * @hide */ interface ApiVersionFactory> { T newInstance(String name, int major, int minor); } }