/** * OpenAtlasForAndroid Project * The MIT License (MIT) Copyright (OpenAtlasForAndroid) 2015 Bunny Blue,achellies * <p> * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following conditions: * <p> * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * <p> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * @author BunnyBlue **/ package com.openatlas.runtime; import android.content.pm.PackageInfo; import android.os.Environment; import android.os.Handler; import android.os.StatFs; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.openatlas.bundleInfo.BundleInfoList; import com.openatlas.framework.OpenAtlas; import com.openatlas.framework.AtlasConfig; import com.openatlas.framework.BundleImpl; import com.openatlas.framework.Framework; import com.openatlas.framework.OpenAtlasInternalConstant; import com.openatlas.log.Logger; import com.openatlas.log.LoggerFactory; import com.openatlas.log.OpenAtlasMonitor; import org.osgi.framework.Bundle; import java.io.File; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /***Load class From Bundle**/ public class ClassLoadFromBundle { private static final String TAG = "ClassLoadFromBundle"; private static Hashtable<Integer, String> classNotFoundReason; private static int reasonCnt; public static List<String> sInternalBundles; static ZipFile sZipFile = null; static Logger log; static { classNotFoundReason = new Hashtable<Integer, String>(); reasonCnt = 0; log = LoggerFactory.getInstance("ClassLoadFromBundle"); } public static String getClassNotFoundReason(String className) { for (int i = 0; i < classNotFoundReason.size(); i++) { if ((classNotFoundReason.get(Integer.valueOf(i)) + "").contains(className + "")) { return classNotFoundReason.get(Integer.valueOf(i)) + ""; } } return ""; } private static void insertToReasonList(String className, String reason) { classNotFoundReason.put(Integer.valueOf(reasonCnt), " Not found class " + className + " because " + reason); int i = reasonCnt + 1; reasonCnt = i; reasonCnt = i % 10; } public static String getPackageNameFromEntryName(String pkgName) { String archive = "lib/" + AtlasConfig.PRELOAD_DIR + "/lib"; return pkgName.substring(pkgName.indexOf(archive) + archive.length(), pkgName.indexOf(".so")).replace("_", "."); } public static synchronized void resolveInternalBundles() { synchronized (ClassLoadFromBundle.class) { if (sInternalBundles == null || sInternalBundles.size() == 0) { String str = "lib/" + AtlasConfig.PRELOAD_DIR + "/libcom_"; List<String> arrayList = new ArrayList<String>(); try { sZipFile = new ZipFile(RuntimeVariables.androidApplication.getApplicationInfo().sourceDir); Enumeration<?> entries = sZipFile.entries(); while (entries.hasMoreElements()) { String name = ((ZipEntry) entries.nextElement()).getName(); if (name.startsWith(str) && name.endsWith(".so")) { arrayList.add(getPackageNameFromEntryName(name)); } } sInternalBundles = arrayList; } catch (Exception e) { Log.e(TAG, "Exception while get bundles in assets or lib", e); } } } } static Class<?> loadFromInstalledBundles(String componet) throws ClassNotFoundException { BundleImpl bundleImpl; Class<?> cls = null; List<Bundle> bundles = Framework.getBundles(); if (!(bundles == null || bundles.isEmpty())) { for (Bundle bundle : bundles) { bundleImpl = (BundleImpl) bundle; PackageLite packageLite = DelegateComponent.getPackage(bundleImpl.getLocation()); if (packageLite != null && packageLite.components.contains(componet)) { bundleImpl.getArchive().optDexFile(); ClassLoader classLoader = bundleImpl.getClassLoader(); if (classLoader != null) { try { cls = classLoader.loadClass(componet); if (cls != null) { return cls; } } catch (ClassNotFoundException e) { throw new ClassNotFoundException("Can't find class " + componet + " in BundleClassLoader: " + bundleImpl.getLocation() + " [" + (bundles == null ? 0 : bundles.size()) + "]" + "classloader is: " + (classLoader == null ? "null" : "not null") + " packageversion " + getPackageVersion() + " exception:" + e.getMessage()); } } StringBuilder append = new StringBuilder().append("Can't find class ").append(componet) .append(" in BundleClassLoader: ").append(bundleImpl.getLocation()).append(" ["); throw new ClassNotFoundException(append.append( bundles.size()).append("]") .append(classLoader == null ? "classloader is null" : "classloader not null") .append(" packageversion ").append(getPackageVersion()).toString()); } } } if (!(bundles == null || bundles.isEmpty())) { for (Bundle bundle : Framework.getBundles()) { bundleImpl = (BundleImpl) bundle; if (bundleImpl.getArchive().isDexOpted()) { ClassLoader classLoader = bundleImpl.getClassLoader(); if (classLoader != null) { try { cls = classLoader.loadClass(componet); if (cls != null) { return cls; } } catch (ClassNotFoundException e) { } } else { } } } } return cls; } /**** * check bundle install and dependency * if not install ,check bundle file exits on local file system, if not install from host app *****/ public static void checkInstallBundleAndDependency(String location) { List<String> dependencyForBundle = BundleInfoList.getInstance().getDependencyForBundle(location); if (dependencyForBundle != null && dependencyForBundle.size() > 0) { for (int i = 0; i < dependencyForBundle.size(); i++) { checkInstallBundleAndDependency(dependencyForBundle.get(i)); } } if (OpenAtlas.getInstance().getBundle(location) == null) { String concat = "lib".concat(location.replace(".", "_")).concat(".so"); File file = new File(new File(Framework.getProperty(OpenAtlasInternalConstant.ATLAS_APP_DIRECTORY), "lib"), concat); if (file.exists()) { try { if (checkAvailableDisk()) { OpenAtlas.getInstance().installBundle(location, file); return; } log.error("disk size not enough"); OpenAtlasMonitor.getInstance().trace(Integer.valueOf(-1), location, "", "disk size not enough"); } catch (Throwable e) { log.error("failed to install bundle " + location, e); OpenAtlasMonitor.getInstance().trace(Integer.valueOf(-1), location, "", "failed to install bundle ", e); throw new RuntimeException("OpenAtlas failed to install bundle " + location, e); } return; } if (sInternalBundles==null){ resolveInternalBundles(); } if (sInternalBundles == null || !sInternalBundles.contains(location)) { log.error(" can not find the library " + concat + " for bundle" + location); OpenAtlasMonitor.getInstance().trace(Integer.valueOf(-1), "" + location, "", "can not find the library " + concat); } else { installFromApkZip(location, concat); } } } /*** * Install bundle from host app **/ private static void installFromApkZip(String location, String fileName) { try { if (checkAvailableDisk()) { OpenAtlas.getInstance().installBundle(location, sZipFile.getInputStream(sZipFile.getEntry("lib/"+AtlasConfig.PRELOAD_DIR+"/" + fileName))); } } catch (Exception e) { log.debug("Failed to install bundle " + fileName + " from APK zipfile "); e.printStackTrace(); } } public static void checkInstallBundleIfNeed(String componet) { synchronized (componet) { if (sInternalBundles == null) { resolveInternalBundles(); } String location = BundleInfoList.getInstance().getBundleNameForComponet(componet); if (TextUtils.isEmpty(location)) { Log.e(TAG, "Failed to find the bundle in BundleInfoList for component " + componet); insertToReasonList(componet, "not found in BundleInfoList!"); } if (sInternalBundles == null || sInternalBundles.contains(location)) { checkInstallBundleAndDependency(location); return; } } } private static long getAvailableInternalMemorySize() { StatFs statFs = new StatFs(Environment.getDataDirectory().getPath()); return ((long) statFs.getAvailableBlocks()) * ((long) statFs.getBlockSize()); } private static boolean checkAvailableDisk() { if (getAvailableInternalMemorySize() >= 2097152) { return true; } new Handler().post(new Runnable() { public void run() { Toast.makeText(RuntimeVariables.androidApplication, "checkAvailableDisk error", Toast.LENGTH_SHORT) .show(); } }); return false; } private static int getPackageVersion() { PackageInfo packageInfo; try { packageInfo = RuntimeVariables.androidApplication.getPackageManager().getPackageInfo( RuntimeVariables.androidApplication.getPackageName(), 0); } catch (Throwable e) { Log.e(TAG, "Error to get PackageInfo >>>", e); packageInfo = new PackageInfo(); } return packageInfo.versionCode; } }