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