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_FAILED_INVALID_APK;
19 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
20 
21 import android.content.pm.PackageParser.PackageParserException;
22 import android.content.pm.parsing.ApkLiteParseUtils;
23 import android.content.pm.parsing.PackageLite;
24 import android.content.pm.parsing.ParsingPackageUtils;
25 import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
26 import android.content.res.ApkAssets;
27 import android.content.res.AssetManager;
28 import android.os.Build;
29 
30 import com.android.internal.util.ArrayUtils;
31 
32 import libcore.io.IoUtils;
33 
34 import java.io.IOException;
35 
36 /**
37  * Loads the base and split APKs into a single AssetManager.
38  * @hide
39  */
40 public class DefaultSplitAssetLoader implements SplitAssetLoader {
41     private final String mBaseApkPath;
42     private final String[] mSplitApkPaths;
43     private final @ParseFlags int mFlags;
44     private AssetManager mCachedAssetManager;
45 
46     private ApkAssets mBaseApkAssets;
47 
DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags)48     public DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
49         mBaseApkPath = pkg.getBaseApkPath();
50         mSplitApkPaths = pkg.getSplitApkPaths();
51         mFlags = flags;
52     }
53 
loadApkAssets(String path, @ParseFlags int flags)54     private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
55             throws PackageParserException {
56         if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
57                 && !ApkLiteParseUtils.isApkPath(path)) {
58             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
59                     "Invalid package file: " + path);
60         }
61 
62         try {
63             return ApkAssets.loadFromPath(path);
64         } catch (IOException e) {
65             throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
66                     "Failed to load APK at path " + path, e);
67         }
68     }
69 
70     @Override
getBaseAssetManager()71     public AssetManager getBaseAssetManager() throws PackageParserException {
72         if (mCachedAssetManager != null) {
73             return mCachedAssetManager;
74         }
75 
76         ApkAssets[] apkAssets = new ApkAssets[(mSplitApkPaths != null
77                 ? mSplitApkPaths.length : 0) + 1];
78 
79         // Load the base.
80         int splitIdx = 0;
81         apkAssets[splitIdx++] = mBaseApkAssets = loadApkAssets(mBaseApkPath, mFlags);
82 
83         // Load any splits.
84         if (!ArrayUtils.isEmpty(mSplitApkPaths)) {
85             for (String apkPath : mSplitApkPaths) {
86                 apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
87             }
88         }
89 
90         AssetManager assets = new AssetManager();
91         assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
92                 Build.VERSION.RESOURCES_SDK_INT);
93         assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
94 
95         mCachedAssetManager = assets;
96         return mCachedAssetManager;
97     }
98 
99     @Override
getSplitAssetManager(int splitIdx)100     public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
101         return getBaseAssetManager();
102     }
103 
104     @Override
getBaseApkAssets()105     public ApkAssets getBaseApkAssets() {
106         return mBaseApkAssets;
107     }
108 
109     @Override
close()110     public void close() throws Exception {
111         IoUtils.closeQuietly(mCachedAssetManager);
112     }
113 }
114