blob: ea18fd3bed5c9f9ebe5e860e28fbcbd5c10302ab [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.apksig.apk;
import com.android.apksig.internal.util.Pair;
import com.android.apksig.internal.zip.ZipUtils;
import com.android.apksig.util.DataSource;
import com.android.apksig.zip.ZipFormatException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* APK utilities.
*/
public class ApkUtils {
private ApkUtils() {}
/**
* Finds the main ZIP sections of the provided APK.
*
* @throws IOException if an I/O error occurred while reading the APK
* @throws ZipFormatException if the APK is malformed
*/
public static ZipSections findZipSections(DataSource apk)
throws IOException, ZipFormatException {
Pair<ByteBuffer, Long> eocdAndOffsetInFile =
ZipUtils.findZipEndOfCentralDirectoryRecord(apk);
if (eocdAndOffsetInFile == null) {
throw new ZipFormatException("ZIP End of Central Directory record not found");
}
ByteBuffer eocdBuf = eocdAndOffsetInFile.getFirst();
long eocdOffset = eocdAndOffsetInFile.getSecond();
if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apk, eocdOffset)) {
throw new ZipFormatException("ZIP64 APK not supported");
}
eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
long cdStartOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocdBuf);
if (cdStartOffset >= eocdOffset) {
throw new ZipFormatException(
"ZIP Central Directory start offset out of range: " + cdStartOffset
+ ". ZIP End of Central Directory offset: " + eocdOffset);
}
long cdSizeBytes = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocdBuf);
long cdEndOffset = cdStartOffset + cdSizeBytes;
if (cdEndOffset > eocdOffset) {
throw new ZipFormatException(
"ZIP Central Directory overlaps with End of Central Directory"
+ ". CD end: " + cdEndOffset
+ ", EoCD start: " + eocdOffset);
}
int cdRecordCount = ZipUtils.getZipEocdCentralDirectoryTotalRecordCount(eocdBuf);
return new ZipSections(
cdStartOffset,
cdSizeBytes,
cdRecordCount,
eocdOffset,
eocdBuf);
}
/**
* Information about the ZIP sections of an APK.
*/
public static class ZipSections {
private final long mCentralDirectoryOffset;
private final long mCentralDirectorySizeBytes;
private final int mCentralDirectoryRecordCount;
private final long mEocdOffset;
private final ByteBuffer mEocd;
public ZipSections(
long centralDirectoryOffset,
long centralDirectorySizeBytes,
int centralDirectoryRecordCount,
long eocdOffset,
ByteBuffer eocd) {
mCentralDirectoryOffset = centralDirectoryOffset;
mCentralDirectorySizeBytes = centralDirectorySizeBytes;
mCentralDirectoryRecordCount = centralDirectoryRecordCount;
mEocdOffset = eocdOffset;
mEocd = eocd;
}
/**
* Returns the start offset of the ZIP Central Directory. This value is taken from the
* ZIP End of Central Directory record.
*/
public long getZipCentralDirectoryOffset() {
return mCentralDirectoryOffset;
}
/**
* Returns the size (in bytes) of the ZIP Central Directory. This value is taken from the
* ZIP End of Central Directory record.
*/
public long getZipCentralDirectorySizeBytes() {
return mCentralDirectorySizeBytes;
}
/**
* Returns the number of records in the ZIP Central Directory. This value is taken from the
* ZIP End of Central Directory record.
*/
public int getZipCentralDirectoryRecordCount() {
return mCentralDirectoryRecordCount;
}
/**
* Returns the start offset of the ZIP End of Central Directory record. The record extends
* until the very end of the APK.
*/
public long getZipEndOfCentralDirectoryOffset() {
return mEocdOffset;
}
/**
* Returns the contents of the ZIP End of Central Directory.
*/
public ByteBuffer getZipEndOfCentralDirectory() {
return mEocd;
}
}
/**
* Sets the offset of the start of the ZIP Central Directory in the APK's ZIP End of Central
* Directory record.
*
* @param zipEndOfCentralDirectory APK's ZIP End of Central Directory record
* @param offset offset of the ZIP Central Directory relative to the start of the archive. Must
* be between {@code 0} and {@code 2^32 - 1} inclusive.
*/
public static void setZipEocdCentralDirectoryOffset(
ByteBuffer zipEndOfCentralDirectory, long offset) {
ByteBuffer eocd = zipEndOfCentralDirectory.slice();
eocd.order(ByteOrder.LITTLE_ENDIAN);
ZipUtils.setZipEocdCentralDirectoryOffset(eocd, offset);
}
}