blob: 99a8a4e2e2bed5ddc1af686b67b4d8897dfeca40 [file] [log] [blame]
/*
* Copyright (C) 2016 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 com.android.build.gradle.internal;
import com.android.Version;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLClassLoader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.jar.Manifest;
public final class NonFinalPluginExpiry {
/**
* default retirement age in days since its inception date for RC or beta versions.
*/
@VisibleForTesting static final Period DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE =
Period.ofDays(40);
private NonFinalPluginExpiry() {
}
/**
* Verify that this plugin execution is within its public time range.
*
* @throws RuntimeException if the plugin is a non final plugin older than 40 days.
*/
public static void verifyRetirementAge() {
// disable the time bomb for now.
if (true) return;
URLClassLoader cl = (URLClassLoader) NonFinalPluginExpiry.class.getClassLoader();
try (InputStream inputStream = cl.findResource("META-INF/MANIFEST.MF").openStream()) {
verifyRetirementAge(
LocalDate.now(),
new Manifest(inputStream),
System.getenv("ANDROID_DAILY_OVERRIDE"));
} catch (IOException ignore) {}
}
@VisibleForTesting
static void verifyRetirementAge(
@NonNull LocalDate now,
@NonNull Manifest manifest,
@Nullable String dailyOverride) {
String version = manifest.getMainAttributes().getValue("Plugin-Version");
Period retirementAge = getRetirementAge(version);
// if this plugin version will never be outdated, return.
if (retirementAge == null) {
return;
}
String inceptionDateAttr = manifest.getMainAttributes().getValue("Inception-Date");
// when running in unit tests, etc... the manifest entries are absent.
if (inceptionDateAttr == null) {
return;
}
LocalDate inceptionDate =
LocalDate.parse(inceptionDateAttr, DateTimeFormatter.ISO_LOCAL_DATE);
LocalDate expiryDate = inceptionDate.plus(retirementAge);
if (now.compareTo(expiryDate) > 0) {
// this plugin is too old.
final MessageDigest crypt;
try {
crypt = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
return;
}
crypt.reset();
// encode the day, not the current time.
try {
crypt.update(
String.format(
"%1$s:%2$s:%3$s",
now.getYear(),
now.getMonthValue() -1,
now.getDayOfMonth())
.getBytes("utf8"));
} catch (UnsupportedEncodingException e) {
return;
}
String overrideValue = new BigInteger(1, crypt.digest()).toString(16);
if (dailyOverride == null) {
String message =
String.format(
"The android gradle plugin version %1$s is too old, "
+ "please update to the latest version.\n"
+ "\n"
+ "To override this check from the command line please "
+ "set the ANDROID_DAILY_OVERRIDE environment variable to "
+ "\"%2$s\"",
version,
overrideValue);
System.err.println(message);
throw new AndroidGradlePluginTooOldException(message);
} else {
// allow a version specific override.
String versionOverride =
String.valueOf(Version.ANDROID_GRADLE_PLUGIN_VERSION.hashCode());
if (dailyOverride.equals(overrideValue) || dailyOverride.equals(versionOverride)) {
return;
}
String message =
String.format(
"The android gradle plugin version %1$s is too old,"
+ "please update to the latest version.\n"
+ "\n"
+ "The ANDROID_DAILY_OVERRIDE value is outdated. "
+ "Please set the ANDROID_DAILY_OVERRIDE environment "
+ "variable to \"%2$s\"",
version, overrideValue);
System.err.println(message);
throw new AndroidGradlePluginTooOldException(message);
}
}
}
/**
* Returns the retirement age for this plugin depending on its version string, or null if this
* plugin version will never become obsolete
*
* @param version the plugin full version, like 1.3.4-preview5 or 1.0.2 or 1.2.3-beta4
*/
@Nullable
private static Period getRetirementAge(@Nullable String version) {
if (version == null
|| version.contains("rc")
|| version.contains("beta")
|| version.contains("alpha")
|| version.contains("preview")) {
return DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE;
}
return null;
}
public static final class AndroidGradlePluginTooOldException extends RuntimeException {
public AndroidGradlePluginTooOldException(@NonNull String message) {
super(message);
}
}
}