![JAR search and dependency download from the Maven repository](/logo.png)
com.android.sdklib.repository.local.LocalDirInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdklib Show documentation
Show all versions of sdklib Show documentation
A library to parse and download the Android SDK.
/*
* Copyright (C) 2014 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.sdklib.repository.local;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.sdklib.io.FileOp;
import com.android.sdklib.io.IFileOp;
import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.zip.Adler32;
/**
* Keeps information on a visited directory to quickly determine if it
* has changed later. A directory has changed if its timestamp has been
* modified, or if an underlying source.properties file has changed in
* timestamp or checksum.
*
* Note that depending on the filesystem & OS, the content of the files in
* a directory can change without the directory's last-modified property
* changing. To have a consistent behavior between OSes, we compute a quick
* checksum using all the files & directories modified timestamps.
* The content of files is not included though, except for the checksum on
* the source.property file since this one is the most important for the SDK.
*
* The {@link #hashCode()} and {@link #equals(Object)} methods directly
* defer to the underlying File object. This allows the DirInfo to be placed
* into a map and still call {@link Map#containsKey(Object)} with a File
* object to check whether there's a corresponding DirInfo in the map.
*/
class LocalDirInfo {
@NonNull
private final IFileOp mFileOp;
@NonNull
private final File mDir;
private final long mDirModifiedTS;
private final long mDirChecksum;
private final long mPropsModifiedTS;
private final long mPropsChecksum;
/**
* Creates a new immutable {@link LocalDirInfo}.
*
* @param fileOp The {@link FileOp} to use for all file-based interactions.
* @param dir The platform/addon directory of the target. It should be a directory.
*/
public LocalDirInfo(@NonNull IFileOp fileOp, @NonNull File dir) {
mFileOp = fileOp;
mDir = dir;
mDirModifiedTS = mFileOp.lastModified(dir);
// Capture some info about the source.properties file if it exists.
// We use propsModifiedTS == 0 to mean there is no props file.
long propsChecksum = 0;
long propsModifiedTS = 0;
File props = new File(dir, SdkConstants.FN_SOURCE_PROP);
if (mFileOp.isFile(props)) {
propsModifiedTS = mFileOp.lastModified(props);
propsChecksum = getFileChecksum(props);
}
mPropsModifiedTS = propsModifiedTS;
mPropsChecksum = propsChecksum;
mDirChecksum = getDirChecksum(mDir);
}
/**
* Checks whether the directory/source.properties attributes have changed.
*
* @return True if the directory modified timestamp or
* its source.property files have changed.
*/
public boolean hasChanged() {
// Does platform directory still exist?
if (!mFileOp.isDirectory(mDir)) {
return true;
}
// Has platform directory modified-timestamp changed?
if (mDirModifiedTS != mFileOp.lastModified(mDir)) {
return true;
}
File props = new File(mDir, SdkConstants.FN_SOURCE_PROP);
// The directory did not have a props file if target was null or
// if mPropsModifiedTS is 0.
boolean hadProps = mPropsModifiedTS != 0;
// Was there a props file and it vanished, or there wasn't and there's one now?
if (hadProps != mFileOp.isFile(props)) {
return true;
}
if (hadProps) {
// Has source.props file modified-timestamp changed?
if (mPropsModifiedTS != mFileOp.lastModified(props)) {
return true;
}
// Had the content of source.props changed?
if (mPropsChecksum != getFileChecksum(props)) {
return true;
}
}
// Has the deep directory checksum changed?
if (mDirChecksum != getDirChecksum(mDir)) {
return true;
}
return false;
}
/**
* Computes an adler32 checksum (source.props are small files, so this
* should be OK with an acceptable collision rate.)
*/
private long getFileChecksum(@NonNull File file) {
InputStream fis = null;
try {
fis = mFileOp.newFileInputStream(file);
Adler32 a = new Adler32();
byte[] buf = new byte[1024];
int n;
while ((n = fis.read(buf)) > 0) {
a.update(buf, 0, n);
}
return a.getValue();
} catch (Exception ignore) {
} finally {
try {
if (fis != null) {
fis.close();
}
} catch(Exception ignore) {}
}
return 0;
}
/**
* Computes a checksum using the last-modified attributes of all
* the files and first-leveldirectories in this root directory.
*
* Heuristic: the SDK Manager updates package by replacing whole directories
* so we don't need to do a recursive deep-first checksum of all files. Only
* the top-level of the package directory should be sufficient to detect
* SDK updates.
*/
private long getDirChecksum(@NonNull File dir) {
long checksum = mFileOp.lastModified(dir);
// Get the file & directory list sorted by case-insensitive name
// to make the checksum more consistent.
File[] files = mFileOp.listFiles(dir);
Arrays.sort(files, new Comparator() {
@Override
public int compare(File o1, File o2) {
return o1.getName().compareToIgnoreCase(o2.getName());
}
});
for (File file : files) {
checksum = 31 * checksum | mFileOp.lastModified(file);
}
return checksum;
}
/** Returns a visual representation of this object for debugging. */
@Override
public String toString() {
String s = String.format(""; //$NON-NLS-1$
}
/**
* Returns the hashCode of the underlying File object.
*
* When a {@link LocalDirInfo} is placed in a map, what matters is to use the underlying
* File object as the key so {@link #hashCode()} and {@link #equals(Object)} both
* return the properties of the underlying File object.
*
* @see File#hashCode()
*/
@Override
public int hashCode() {
return mDir.hashCode();
}
/**
* Checks equality of the underlying File object.
*
* When a {@link LocalDirInfo} is placed in a map, what matters is to use the underlying
* File object as the key so {@link #hashCode()} and {@link #equals(Object)} both
* return the properties of the underlying File object.
*
* @see File#equals(Object)
*/
@Override
public boolean equals(Object obj) {
return mDir.equals(obj);
};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy