blob: 58eaabfa62f2ff0625ce7fdf524a4c8f3fecf16c [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
2 * Copyright (C) 2016 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 */
16package android.content.pm.split;
17
18import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
19
20import android.annotation.NonNull;
21import android.content.pm.PackageManager;
22import android.content.pm.PackageParser;
23import android.content.pm.PackageParser.PackageParserException;
24import android.content.pm.PackageParser.ParseFlags;
25import android.content.res.ApkAssets;
26import android.content.res.AssetManager;
27import android.os.Build;
28import android.util.SparseArray;
29
30import libcore.io.IoUtils;
31
32import java.io.IOException;
33import java.util.ArrayList;
34import java.util.Collections;
35
36/**
37 * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation
38 * is to be used when an application opts-in to isolated split loading.
39 * @hide
40 */
41public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
42 implements SplitAssetLoader {
43 private final String[] mSplitPaths;
44 private final @ParseFlags int mFlags;
45 private final ApkAssets[][] mCachedSplitApks;
46 private final AssetManager[] mCachedAssetManagers;
47
48 public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
49 SparseArray<int[]> dependencies, @ParseFlags int flags) {
50 super(dependencies);
51
52 // The base is inserted into index 0, so we need to shift all the splits by 1.
53 mSplitPaths = new String[pkg.splitCodePaths.length + 1];
54 mSplitPaths[0] = pkg.baseCodePath;
55 System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
56
57 mFlags = flags;
58 mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
59 mCachedAssetManagers = new AssetManager[mSplitPaths.length];
60 }
61
62 @Override
63 protected boolean isSplitCached(int splitIdx) {
64 return mCachedAssetManagers[splitIdx] != null;
65 }
66
67 private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
68 throws PackageParserException {
69 if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
70 throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
71 "Invalid package file: " + path);
72 }
73
74 try {
75 return ApkAssets.loadFromPath(path);
76 } catch (IOException e) {
77 throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
78 "Failed to load APK at path " + path, e);
79 }
80 }
81
82 private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
83 final AssetManager assets = new AssetManager();
84 assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85 Build.VERSION.RESOURCES_SDK_INT);
86 assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
87 return assets;
88 }
89
90 @Override
91 protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
92 int parentSplitIdx) throws PackageParserException {
93 final ArrayList<ApkAssets> assets = new ArrayList<>();
94
95 // Include parent ApkAssets.
96 if (parentSplitIdx >= 0) {
97 Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
98 }
99
100 // Include this ApkAssets.
101 assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
102
103 // Load and include all config splits for this feature.
104 for (int configSplitIdx : configSplitIndices) {
105 assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
106 }
107
108 // Cache the results.
109 mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
110 mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
111 }
112
113 @Override
114 public AssetManager getBaseAssetManager() throws PackageParserException {
115 loadDependenciesForSplit(0);
116 return mCachedAssetManagers[0];
117 }
118
119 @Override
120 public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
121 // Since we insert the base at position 0, and PackageParser keeps splits separate from
122 // the base, we need to adjust the index.
123 loadDependenciesForSplit(idx + 1);
124 return mCachedAssetManagers[idx + 1];
125 }
126
127 @Override
128 public void close() throws Exception {
129 for (AssetManager assets : mCachedAssetManagers) {
130 IoUtils.closeQuietly(assets);
131 }
132 }
133}