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.cts.releaseparser;
18 
19 import com.android.cts.releaseparser.ReleaseProto.*;
20 
21 import java.io.File;
22 import java.io.RandomAccessFile;
23 import java.util.Arrays;
24 import java.util.logging.Logger;
25 
26 // art/runtime/vdex_file.h & vdex_file.cc
27 public class VdexParser extends FileParser {
28     // The magic values for the VDEX identification.
29     private static final byte[] VDEX_MAGIC = {(byte) 'v', (byte) 'd', (byte) 'e', (byte) 'x'};
30     private static final int HEADER_SIZE = 64;
31     private VdexInfo.Builder mVdexInfoBuilder;
32 
VdexParser(File file)33     public VdexParser(File file) {
34         super(file);
35     }
36 
37     @Override
getType()38     public Entry.EntryType getType() {
39         return Entry.EntryType.VDEX;
40     }
41 
42     @Override
setAdditionalInfo()43     public void setAdditionalInfo() {
44         getFileEntryBuilder().setVdexInfo(getVdexInfo());
45     }
46 
47     @Override
getCodeId()48     public String getCodeId() {
49         if (mVdexInfoBuilder == null) {
50             parse();
51         }
52         return mCodeId;
53     }
54 
getVdexInfo()55     public VdexInfo getVdexInfo() {
56         if (mVdexInfoBuilder == null) {
57             parse();
58         }
59         return mVdexInfoBuilder.build();
60     }
61 
parse()62     private void parse() {
63         byte[] buffer = new byte[HEADER_SIZE];
64         mVdexInfoBuilder = VdexInfo.newBuilder();
65 
66         try {
67             RandomAccessFile raFile = new RandomAccessFile(getFile(), "r");
68             raFile.seek(0);
69             raFile.readFully(buffer, 0, HEADER_SIZE);
70             raFile.close();
71 
72             // ToDo: this is specific for 019 VerifierDepsVersion. Need to handle changes for older
73             // versions
74             if (buffer[0] != VDEX_MAGIC[0]
75                     || buffer[1] != VDEX_MAGIC[1]
76                     || buffer[2] != VDEX_MAGIC[2]
77                     || buffer[3] != VDEX_MAGIC[3]) {
78                 String content = new String(buffer);
79                 System.err.println("Invalid VDEX file:" + getFileName() + " " + content);
80                 throw new IllegalArgumentException("Invalid VDEX MAGIC");
81             }
82             int offset = 4;
83             String version = new String(Arrays.copyOfRange(buffer, offset, offset + 4));
84             mVdexInfoBuilder.setVerifierDepsVersion(version);
85             offset += 4;
86             String dex_section_version = new String(Arrays.copyOfRange(buffer, offset, offset + 4));
87             mVdexInfoBuilder.setDexSectionVersion(dex_section_version);
88             offset += 4;
89             int numberOfDexFiles = getIntLittleEndian(buffer, offset);
90             mVdexInfoBuilder.setNumberOfDexFiles(numberOfDexFiles);
91             offset += 4;
92             mVdexInfoBuilder.setVerifierDepsSize(getIntLittleEndian(buffer, offset));
93             offset += 4;
94 
95             // Code Id format: [0xchecksum1],...
96             StringBuilder codeIdSB = new StringBuilder();
97             for (int i = 0; i < numberOfDexFiles; i++) {
98                 int checksums = getIntLittleEndian(buffer, offset);
99                 offset += 4;
100                 mVdexInfoBuilder.addChecksums(checksums);
101                 codeIdSB.append(String.format(CODE_ID_FORMAT, checksums));
102             }
103             mCodeId = codeIdSB.toString();
104 
105             for (int i = 0; i < numberOfDexFiles; i++) {
106                 DexSectionHeader.Builder dshBuilder = DexSectionHeader.newBuilder();
107                 dshBuilder.setDexSize(getIntLittleEndian(buffer, offset));
108                 offset += 4;
109                 dshBuilder.setDexSharedDataSize(getIntLittleEndian(buffer, offset));
110                 offset += 4;
111                 dshBuilder.setQuickeningInfoSize(getIntLittleEndian(buffer, offset));
112                 offset += 4;
113                 mVdexInfoBuilder.addDexSectionHeaders(dshBuilder.build());
114                 offset += 4;
115             }
116 
117             for (int i = 0; i < numberOfDexFiles; i++) {
118                 int quicken_table_off = getIntLittleEndian(buffer, offset);
119                 offset += 4;
120                 // Todo processing Dex
121             }
122 
123         } catch (Exception ex) {
124             System.err.println("Invalid VDEX file:" + getFileName());
125             mVdexInfoBuilder.setValid(false);
126         }
127     }
128 
129     private static final String USAGE_MESSAGE =
130             "Usage: java -jar releaseparser.jar "
131                     + VdexParser.class.getCanonicalName()
132                     + " [-options <parameter>]...\n"
133                     + "           to parse VDEX file meta data\n"
134                     + "Options:\n"
135                     + "\t-i PATH\t The file path of the file to be parsed.\n"
136                     + "\t-of PATH\t The file path of the output file instead of printing to System.out.\n";
137 
main(String[] args)138     public static void main(String[] args) {
139         try {
140             ArgumentParser argParser = new ArgumentParser(args);
141             String fileName = argParser.getParameterElement("i", 0);
142             String outputFileName = argParser.getParameterElement("of", 0);
143 
144             File aFile = new File(fileName);
145             VdexParser aParser = new VdexParser(aFile);
146             Entry fileEntry = aParser.getFileEntryBuilder().build();
147             writeTextFormatMessage(outputFileName, fileEntry);
148         } catch (Exception ex) {
149             System.out.println(USAGE_MESSAGE);
150             ex.printStackTrace();
151         }
152     }
153 
getLogger()154     private static Logger getLogger() {
155         return Logger.getLogger(VdexParser.class.getSimpleName());
156     }
157 }
158