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 } 134