blob: 0d9149a10fee8f2645407cdc4ac042c9dc71096a [file] [log] [blame]
/*
* Copyright (C) 2020 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.tools.agent.app.inspection.version;
import com.android.tools.agent.app.inspection.ClassLoaderUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.ConcurrentHashMap;
/**
* Opens a stream to the targeted version file and reads its first line.
*
* <p>This class caches the version strings by their file names.
*/
class VersionFileReader {
/** Result object representing the outcome of the read. */
static class Result {
enum Status {
/** Read was successful. Version string was obtained. */
SUCCESS,
/** Version file was not found. */
NOT_FOUND,
/** An error was encountered while reading the version file. */
READ_ERROR
}
public Status status;
/** The version string if the read was successful. Otherwise null. */
public String versionString;
public Result(Status status, String versionString) {
this.status = status;
this.versionString = versionString;
}
}
private final ConcurrentHashMap<String, Result> mVersions = new ConcurrentHashMap<>();
/**
* Reads the specified version file in the APK's META-INF directory.
*
* <p>This uses the main class loader and will throw a runtime exception if it's not found.
*
* @param versionFile the full name of the version file
* @return input stream of the version file
*/
private static InputStream loadVersionFile(String versionFile) {
ClassLoader mainClassLoader = ClassLoaderUtils.mainThreadClassLoader();
if (mainClassLoader == null) {
throw new RuntimeException("main class loader not found");
}
return mainClassLoader.getResourceAsStream("META-INF/" + versionFile);
}
/**
* Reads the first line of the provided input stream of the version file.
*
* <p>This assumes the first and only line of the file is the version string. Format is the same
* as what Gradle uses, e.g. 1.2.3-alpha04 See also:
* https://docs.gradle.org/current/userguide/single_versions.html
*
* @param inputStream the stream of the version file
* @return the first line read. null if IOException was encountered.
*/
private static String readVersionStringOrNull(InputStream inputStream) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
return reader.readLine();
} catch (IOException e) {
return null;
}
}
private Result newResult(Result.Status status, String versionFile, String versionString) {
Result newResult = new Result(status, versionString);
mVersions.putIfAbsent(versionFile, newResult);
return newResult;
}
/**
* Opens an input stream to the provided version file if it exists, and reads its first line.
*
* @param versionFile the file to be read
* @return the result of the read which can be of 3 statuses. SUCCESS, NOT_FOUND, and
* READ_ERROR. See enum declaration for more details.
*/
public Result readVersionFile(String versionFile) {
if (mVersions.containsKey(versionFile)) {
return mVersions.get(versionFile);
}
InputStream inputStream = loadVersionFile(versionFile);
if (inputStream == null) {
return newResult(Result.Status.NOT_FOUND, versionFile, null);
}
String versionString = readVersionStringOrNull(inputStream);
if (versionString == null) {
return newResult(Result.Status.READ_ERROR, versionFile, null);
} else {
return newResult(Result.Status.SUCCESS, versionFile, versionString);
}
}
}