• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.fastdeploy;
18 
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.io.RandomAccessFile;
24 import java.util.Arrays;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Comparator;
28 
29 import com.android.tools.build.apkzlib.zip.ZFile;
30 import com.android.tools.build.apkzlib.zip.ZFileOptions;
31 import com.android.tools.build.apkzlib.zip.StoredEntry;
32 import com.android.tools.build.apkzlib.zip.StoredEntryType;
33 import com.android.tools.build.apkzlib.zip.CentralDirectoryHeaderCompressInfo;
34 import com.android.tools.build.apkzlib.zip.CentralDirectoryHeader;
35 
36 import com.android.fastdeploy.APKMetaData;
37 import com.android.fastdeploy.APKEntry;
38 
39 class PatchUtils {
40     private static final long NEGATIVE_MASK = 1L << 63;
41     private static final long NEGATIVE_LONG_SIGN_MASK = 1L << 63;
42     public static final String SIGNATURE = "HAMADI/IHD";
43 
getOffsetFromEntry(StoredEntry entry)44     private static long getOffsetFromEntry(StoredEntry entry) {
45         return entry.getCentralDirectoryHeader().getOffset() + entry.getLocalHeaderSize();
46     }
47 
getAPKMetaData(File apkFile)48     public static APKMetaData getAPKMetaData(File apkFile) throws IOException {
49         APKMetaData.Builder apkEntriesBuilder = APKMetaData.newBuilder();
50         ZFileOptions options = new ZFileOptions();
51         ZFile zFile = new ZFile(apkFile, options);
52 
53         ArrayList<StoredEntry> metaDataEntries = new ArrayList<StoredEntry>();
54 
55         for (StoredEntry entry : zFile.entries()) {
56             if (entry.getType() != StoredEntryType.FILE) {
57                 continue;
58             }
59             metaDataEntries.add(entry);
60         }
61 
62         Collections.sort(metaDataEntries, new Comparator<StoredEntry>() {
63             private long getOffsetFromEntry(StoredEntry entry) {
64                 return PatchUtils.getOffsetFromEntry(entry);
65             }
66 
67             @Override
68             public int compare(StoredEntry lhs, StoredEntry rhs) {
69                 // -1 - less than, 1 - greater than, 0 - equal, all inversed for descending
70                 return Long.compare(getOffsetFromEntry(lhs), getOffsetFromEntry(rhs));
71             }
72         });
73 
74         for (StoredEntry entry : metaDataEntries) {
75             CentralDirectoryHeader cdh = entry.getCentralDirectoryHeader();
76             CentralDirectoryHeaderCompressInfo cdhci = cdh.getCompressionInfoWithWait();
77 
78             APKEntry.Builder entryBuilder = APKEntry.newBuilder();
79             entryBuilder.setCrc32(cdh.getCrc32());
80             entryBuilder.setFileName(cdh.getName());
81             entryBuilder.setCompressedSize(cdhci.getCompressedSize());
82             entryBuilder.setUncompressedSize(cdh.getUncompressedSize());
83             entryBuilder.setDataOffset(getOffsetFromEntry(entry));
84 
85             apkEntriesBuilder.addEntries(entryBuilder);
86             apkEntriesBuilder.build();
87         }
88         return apkEntriesBuilder.build();
89     }
90 
91     /**
92      * Writes a 64-bit signed integer to the specified {@link OutputStream}. The least significant
93      * byte is written first and the most significant byte is written last.
94      * @param value the value to write
95      * @param outputStream the stream to write to
96      */
writeFormattedLong(final long value, OutputStream outputStream)97     static void writeFormattedLong(final long value, OutputStream outputStream) throws IOException {
98         long y = value;
99         if (y < 0) {
100             y = (-y) | NEGATIVE_MASK;
101         }
102 
103         for (int i = 0; i < 8; ++i) {
104             outputStream.write((byte) (y & 0xff));
105             y >>>= 8;
106         }
107     }
108 
109     /**
110      * Reads a 64-bit signed integer written by {@link #writeFormattedLong(long, OutputStream)} from
111      * the specified {@link InputStream}.
112      * @param inputStream the stream to read from
113      */
readFormattedLong(InputStream inputStream)114     static long readFormattedLong(InputStream inputStream) throws IOException {
115         long result = 0;
116         for (int bitshift = 0; bitshift < 64; bitshift += 8) {
117             result |= ((long) inputStream.read()) << bitshift;
118         }
119 
120         if ((result - NEGATIVE_MASK) > 0) {
121             result = (result & ~NEGATIVE_MASK) * -1;
122         }
123         return result;
124     }
125 
readBsdiffLong(InputStream in)126     static final long readBsdiffLong(InputStream in) throws PatchFormatException, IOException {
127         long result = 0;
128         for (int bitshift = 0; bitshift < 64; bitshift += 8) {
129             result |= ((long) in.read()) << bitshift;
130         }
131 
132         if (result == NEGATIVE_LONG_SIGN_MASK) {
133             // "Negative zero", which is valid in signed-magnitude format.
134             // NB: No sane patch generator should ever produce such a value.
135             throw new PatchFormatException("read negative zero");
136         }
137 
138         if ((result & NEGATIVE_LONG_SIGN_MASK) != 0) {
139             result = -(result & ~NEGATIVE_LONG_SIGN_MASK);
140         }
141 
142         return result;
143     }
144 
readFully(final InputStream in, final byte[] destination, final int startAt, final int numBytes)145     static void readFully(final InputStream in, final byte[] destination, final int startAt,
146         final int numBytes) throws IOException {
147         int numRead = 0;
148         while (numRead < numBytes) {
149             int readNow = in.read(destination, startAt + numRead, numBytes - numRead);
150             if (readNow == -1) {
151                 throw new IOException("truncated input stream");
152             }
153             numRead += readNow;
154         }
155     }
156 
pipe(final InputStream in, final OutputStream out, final byte[] buffer, long copyLength)157     static void pipe(final InputStream in, final OutputStream out, final byte[] buffer,
158         long copyLength) throws IOException {
159         while (copyLength > 0) {
160             int maxCopy = Math.min(buffer.length, (int) copyLength);
161             readFully(in, buffer, 0, maxCopy);
162             out.write(buffer, 0, maxCopy);
163             copyLength -= maxCopy;
164         }
165     }
166 
pipe(final RandomAccessFile in, final OutputStream out, final byte[] buffer, long copyLength)167     static void pipe(final RandomAccessFile in, final OutputStream out, final byte[] buffer,
168         long copyLength) throws IOException {
169         while (copyLength > 0) {
170             int maxCopy = Math.min(buffer.length, (int) copyLength);
171             in.readFully(buffer, 0, maxCopy);
172             out.write(buffer, 0, maxCopy);
173             copyLength -= maxCopy;
174         }
175     }
176 
fill(byte value, final OutputStream out, final byte[] buffer, long fillLength)177     static void fill(byte value, final OutputStream out, final byte[] buffer, long fillLength)
178         throws IOException {
179         while (fillLength > 0) {
180             int maxCopy = Math.min(buffer.length, (int) fillLength);
181             Arrays.fill(buffer, 0, maxCopy, value);
182             out.write(buffer, 0, maxCopy);
183             fillLength -= maxCopy;
184         }
185     }
186 }
187