14e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski/*
24e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * Copyright (C) 2016 The Android Open Source Project
34e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski *
44e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
54e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * you may not use this file except in compliance with the License.
64e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * You may obtain a copy of the License at
74e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski *
84e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
94e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski *
104e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * Unless required by applicable law or agreed to in writing, software
114e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
124e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * See the License for the specific language governing permissions and
144e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * limitations under the License.
154e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski */
164e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskipackage android.content.pm.split;
174e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
184e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskiimport static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
194e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskiimport static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
204e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
211665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinskiimport android.annotation.NonNull;
224e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskiimport android.content.pm.PackageParser;
234e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskiimport android.content.res.AssetManager;
244e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskiimport android.os.Build;
251665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinskiimport android.util.SparseArray;
264e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
274e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskiimport libcore.io.IoUtils;
284e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
294e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskiimport java.util.ArrayList;
304e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskiimport java.util.Collections;
314e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
324e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski/**
334e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation
344e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * is to be used when an application opts-in to isolated split loading.
354e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski * @hide
364e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski */
374e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinskipublic class SplitAssetDependencyLoader
381665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        extends SplitDependencyLoader<PackageParser.PackageParserException>
394e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        implements SplitAssetLoader {
404e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    private final String[] mSplitPaths;
414e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    private final int mFlags;
424e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
431665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski    private String[][] mCachedPaths;
444e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    private AssetManager[] mCachedAssetManagers;
454e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
461665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski    public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
471665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski            SparseArray<int[]> dependencies, int flags) {
484e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        super(dependencies);
491665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski
501665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        // The base is inserted into index 0, so we need to shift all the splits by 1.
511665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        mSplitPaths = new String[pkg.splitCodePaths.length + 1];
521665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        mSplitPaths[0] = pkg.baseCodePath;
531665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
541665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski
554e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        mFlags = flags;
561665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        mCachedPaths = new String[mSplitPaths.length][];
571665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        mCachedAssetManagers = new AssetManager[mSplitPaths.length];
584e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    }
594e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
604e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    @Override
614e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    protected boolean isSplitCached(int splitIdx) {
621665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        return mCachedAssetManagers[splitIdx] != null;
634e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    }
644e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
654e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
664e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski            throws PackageParser.PackageParserException {
674e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        final AssetManager assets = new AssetManager();
684e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        try {
69408afbf06040ea29d1a9d60e9dc50d1923068de4Romain Guy            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
704e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                    Build.VERSION.RESOURCES_SDK_INT);
714e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
724e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski            for (String assetPath : assetPaths) {
734e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 &&
744e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                        !PackageParser.isApkPath(assetPath)) {
754e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                    throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
764e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                            "Invalid package file: " + assetPath);
774e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                }
784e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
794e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                if (assets.addAssetPath(assetPath) == 0) {
804e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                    throw new PackageParser.PackageParserException(
814e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                            INSTALL_PARSE_FAILED_BAD_MANIFEST,
824e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                            "Failed adding asset path: " + assetPath);
834e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                }
844e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski            }
854e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski            return assets;
864e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        } catch (Throwable e) {
874e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski            IoUtils.closeQuietly(assets);
884e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski            throw e;
894e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        }
904e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    }
914e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
924e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    @Override
931665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski    protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
941665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski            int parentSplitIdx) throws PackageParser.PackageParserException {
954e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        final ArrayList<String> assetPaths = new ArrayList<>();
961665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        if (parentSplitIdx >= 0) {
971665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski            Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]);
984e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        }
994e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
1004e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        assetPaths.add(mSplitPaths[splitIdx]);
1011665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        for (int configSplitIdx : configSplitIndices) {
1021665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski            assetPaths.add(mSplitPaths[configSplitIdx]);
1031665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        }
1041665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
1051665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx],
1064e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski                mFlags);
1074e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    }
1084e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
1094e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    @Override
1104e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
1111665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        loadDependenciesForSplit(0);
1121665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        return mCachedAssetManagers[0];
1134e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    }
1144e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
1154e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    @Override
1164e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
1171665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        // Since we insert the base at position 0, and PackageParser keeps splits separate from
1181665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        // the base, we need to adjust the index.
1191665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        loadDependenciesForSplit(idx + 1);
1201665d0f028e3a225cb117d3e227bef5c5dace2d4Adam Lesinski        return mCachedAssetManagers[idx + 1];
1214e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    }
1224e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski
1234e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    @Override
1244e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    public void close() throws Exception {
1254e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        for (AssetManager assets : mCachedAssetManagers) {
1264e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski            IoUtils.closeQuietly(assets);
1274e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski        }
1284e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski    }
1294e8628157ad0c8c52e74b720eb0328086272ffdaAdam Lesinski}
130