blob: a9896df78468c951df6bd47e9b2409a29848c5ea [file] [log] [blame]
/*
* Copyright 2013-present Facebook, Inc.
*
* 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.facebook.buck.dalvik;
import com.facebook.buck.java.classes.AbstractFileLike;
import com.facebook.buck.java.classes.FileLike;
import com.google.common.base.Preconditions;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
* Helper to create a "canary" class for the secondary DEX. See {@link #create}.
*/
class CanaryFactory {
private static final String CANARY_PATH_FORMAT = "secondary/dex%02d/Canary.class";
/**
* Produced by compiling the following Java file with JDK 7 with "-target 6 -source 6".
* <pre>
* package secondary.dex01;
* public interface Canary {}
* </pre>
*/
private static final byte[] CANARY_TEMPLATE = {
-54, -2, -70, -66, 0x00, 0x00, 0x00, 0x32,
0x00, 0x05, 0x07, 0x00, 0x03, 0x07, 0x00, 0x04,
0x01, 0x00, 0x16, 0x73, 0x65, 0x63, 0x6f, 0x6e,
0x64, 0x61, 0x72, 0x79, 0x2f, 0x64, 0x65, 0x78,
0x30, 0x31, 0x2f, 0x43, 0x61, 0x6e, 0x61, 0x72,
0x79, 0x01, 0x00, 0x10, 0x6a, 0x61, 0x76, 0x61,
0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x4f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x06, 0x01, 0x00, 0x01,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
/**
* Offset into {@link #CANARY_TEMPLATE} where we find the 2-byte UTF-8 index that must be
* updated for each canary class to change the package.
*/
private static final int CANARY_INDEX_OFFSET = 32;
/**
* Adds a "canary" class to a secondary dex that can be safely loaded on any system.
* This avoids an issue where, during secondary dex loading, we attempt to verify a
* secondary dex by loading an arbitrary class, but the class we try to load isn't
* valid on that system (e.g., it depends on Google Maps, but we are on AOSP).
*
* @param index Index of the current zip (to ensure unique names).
*/
static FileLike create(final int index)
throws IOException {
final byte[] canaryClass = Arrays.copyOf(CANARY_TEMPLATE, CANARY_TEMPLATE.length);
final String canaryIndexStr = String.format("%02d", index);
byte[] canaryIndexBytes = canaryIndexStr.getBytes(Charset.forName("UTF-8"));
Preconditions.checkState(canaryIndexBytes.length == 2,
"Formatted index string should always be 2 bytes.");
System.arraycopy(canaryIndexBytes, 0, canaryClass, CANARY_INDEX_OFFSET, 2);
String relativePath = String.format(CANARY_PATH_FORMAT, index);
return getCanaryClass(relativePath, canaryClass);
}
private static FileLike getCanaryClass(final String relativePath, final byte[] canaryClass) {
return new AbstractFileLike() {
@Override
public File getContainer() {
return new File(":memory:");
}
@Override
public String getRelativePath() {
return relativePath;
}
@Override
public long getSize() {
return canaryClass.length;
}
@Override
public InputStream getInput() {
return new ByteArrayInputStream(canaryClass);
}
};
}
}