blob: 584257b1f6a98f5b7a43adfb56fffbb4a58bf416 [file] [log] [blame]
Calin Juravle3fc56c32017-12-11 18:26:13 -08001/**
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
17package android.content.pm.dex;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.assertTrue;
22import static org.junit.Assert.fail;
23
24import android.content.Context;
25import android.content.pm.PackageManager;
26import android.content.pm.PackageParser;
Calin Juravle10b27eb2018-01-22 12:50:01 -080027import android.content.pm.PackageParser.ApkLite;
Calin Juravle3fc56c32017-12-11 18:26:13 -080028import android.content.pm.PackageParser.Package;
Calin Juravle10b27eb2018-01-22 12:50:01 -080029import android.content.pm.PackageParser.PackageLite;
Calin Juravle3fc56c32017-12-11 18:26:13 -080030import android.content.pm.PackageParser.PackageParserException;
31import android.os.FileUtils;
32import android.support.test.InstrumentationRegistry;
33import android.support.test.runner.AndroidJUnit4;
34import android.test.suitebuilder.annotation.SmallTest;
35
36import com.android.frameworks.coretests.R;
37
38import java.io.File;
Calin Juravle10b27eb2018-01-22 12:50:01 -080039import java.io.FileInputStream;
Calin Juravle3fc56c32017-12-11 18:26:13 -080040import java.io.FileOutputStream;
41import java.io.IOException;
42import java.io.InputStream;
43import java.nio.file.Files;
44import java.util.Map;
45import java.util.zip.ZipEntry;
46import java.util.zip.ZipOutputStream;
47
48import libcore.io.IoUtils;
49
50import org.junit.After;
Calin Juravle10b27eb2018-01-22 12:50:01 -080051import org.junit.Assert;
Calin Juravle3fc56c32017-12-11 18:26:13 -080052import org.junit.Before;
53import org.junit.Test;
54import org.junit.runner.RunWith;
55
56@SmallTest
57@RunWith(AndroidJUnit4.class)
58public class DexMetadataHelperTest {
59 private static final String APK_FILE_EXTENSION = ".apk";
60 private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
61
62 private File mTmpDir = null;
63
64 @Before
65 public void setUp() {
66 mTmpDir = IoUtils.createTemporaryDirectory("DexMetadataHelperTest");
67 }
68
69 @After
70 public void tearDown() {
71 if (mTmpDir != null) {
72 File[] files = mTmpDir.listFiles();
73 for (File f : files) {
74 f.delete();
75 }
76 }
77 }
78
79 private File createDexMetadataFile(String apkFileName) throws IOException {
80 File dmFile = new File(mTmpDir, apkFileName.replace(APK_FILE_EXTENSION,
81 DEX_METADATA_FILE_EXTENSION));
82 try (FileOutputStream fos = new FileOutputStream(dmFile)) {
83 try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
84 zipOs.putNextEntry(new ZipEntry("primary.prof"));
85 zipOs.closeEntry();
86 }
87 }
88 return dmFile;
89 }
90
91 private File copyApkToToTmpDir(String apkFileName, int apkResourceId) throws IOException {
92 Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
93 File outFile = new File(mTmpDir, apkFileName);
94 try (InputStream is = context.getResources().openRawResource(apkResourceId)) {
95 FileUtils.copyToFileOrThrow(is, outFile);
96 }
97 return outFile;
98 }
99
100 @Test
101 public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
102 copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
103 createDexMetadataFile("install_split_base.apk");
104 Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
105
106 Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
107 assertEquals(1, packageDexMetadata.size());
108 String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
109 assertNotNull(baseDexMetadata);
110 assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
111 }
112
113 @Test
114 public void testParsePackageSplitsWithDmFileValid()
115 throws IOException, PackageParserException {
116 copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
117 copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
118 createDexMetadataFile("install_split_base.apk");
119 createDexMetadataFile("install_split_feature_a.apk");
120 Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
121
122 Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
123 assertEquals(2, packageDexMetadata.size());
124 String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
125 assertNotNull(baseDexMetadata);
126 assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
127
128 String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
129 assertNotNull(splitDexMetadata);
130 assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
131 }
132
133 @Test
134 public void testParsePackageSplitsNoBaseWithDmFileValid()
135 throws IOException, PackageParserException {
136 copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
137 copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
138 createDexMetadataFile("install_split_feature_a.apk");
139 Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
140
141 Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
142 assertEquals(1, packageDexMetadata.size());
143
144 String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
145 assertNotNull(splitDexMetadata);
146 assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
147 }
148
149 @Test
150 public void testParsePackageWithDmFileInvalid() throws IOException {
151 copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
152 File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
153 Files.createFile(invalidDmFile.toPath());
154 try {
155 PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
156 DexMetadataHelper.validatePackageDexMetadata(pkg);
157 } catch (PackageParserException e) {
158 assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
159 }
160 }
161
162 @Test
163 public void testParsePackageSplitsWithDmFileInvalid()
164 throws IOException, PackageParserException {
165 copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
166 copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
167 createDexMetadataFile("install_split_base.apk");
168 File invalidDmFile = new File(mTmpDir, "install_split_feature_a.dm");
169 Files.createFile(invalidDmFile.toPath());
170
171 try {
172 PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
173 DexMetadataHelper.validatePackageDexMetadata(pkg);
174 } catch (PackageParserException e) {
175 assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
176 }
177 }
178
179 @Test
180 public void testPackageWithDmFileNoMatch() throws IOException {
181 copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
182 createDexMetadataFile("non_existent.apk");
183
184 try {
185 DexMetadataHelper.validateDexPaths(mTmpDir.list());
186 fail("Should fail validation");
187 } catch (IllegalStateException e) {
188 // expected.
189 }
190 }
191
192 @Test
193 public void testPackageSplitsWithDmFileNoMatch()
194 throws IOException, PackageParserException {
195 copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
196 copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
197 createDexMetadataFile("install_split_base.apk");
198 createDexMetadataFile("install_split_feature_a.mistake.apk");
199
200 try {
201 DexMetadataHelper.validateDexPaths(mTmpDir.list());
202 fail("Should fail validation");
203 } catch (IllegalStateException e) {
204 // expected.
205 }
206 }
207
Calin Juravle10b27eb2018-01-22 12:50:01 -0800208 @Test
209 public void testPackageSizeWithDmFile()
210 throws IOException, PackageParserException {
211 copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
212 File dm = createDexMetadataFile("install_split_base.apk");
213 PackageParser.PackageLite pkg = new PackageParser().parsePackageLite(mTmpDir,
214 0 /* flags */);
215
216 Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkg));
217 }
218
219 // This simulates the 'adb shell pm install' flow.
220 @Test
221 public void testPackageSizeWithPartialPackageLite() throws IOException, PackageParserException {
222 File base = copyApkToToTmpDir("install_split_base", R.raw.install_split_base);
223 File dm = createDexMetadataFile("install_split_base.apk");
224 try (FileInputStream is = new FileInputStream(base)) {
225 ApkLite baseApk = PackageParser.parseApkLite(is.getFD(), base.getAbsolutePath(), 0);
226 PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
227 null, null);
228 Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
229 }
230
231 }
232
Calin Juravle3fc56c32017-12-11 18:26:13 -0800233 private static boolean isDexMetadataForApk(String dmaPath, String apkPath) {
234 return apkPath.substring(0, apkPath.length() - APK_FILE_EXTENSION.length()).equals(
235 dmaPath.substring(0, dmaPath.length() - DEX_METADATA_FILE_EXTENSION.length()));
236 }
237}