Alan Viverette | 3da604b | 2020-06-10 18:34:39 +0000 | [diff] [blame] | 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; |
| 23 | import android.content.pm.PackageParser.PackageParserException; |
| 24 | import android.content.pm.PackageParser.ParseFlags; |
| 25 | import android.content.res.ApkAssets; |
| 26 | import android.content.res.AssetManager; |
| 27 | import android.os.Build; |
| 28 | import android.util.SparseArray; |
| 29 | |
| 30 | import libcore.io.IoUtils; |
| 31 | |
| 32 | import java.io.IOException; |
| 33 | import java.util.ArrayList; |
| 34 | import 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 | */ |
| 41 | public 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 | } |