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