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