/* * * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION * * 1. Definitions. * * "License" shall mean the terms and conditions for use, reproduction, * and distribution as defined by Sections 1 through 9 of this document. * * "Licensor" shall mean the copyright owner or entity authorized by * the copyright owner that is granting the License. * * "Legal Entity" shall mean the union of the acting entity and all * other entities that control, are controlled by, or are under common * control with that entity. For the purposes of this definition, * "control" means (i) the power, direct or indirect, to cause the * direction or management of such entity, whether by contract or * otherwise, or (ii) ownership of fifty percent (50%) or more of the * outstanding shares, or (iii) beneficial ownership of such entity. * * "You" (or "Your") shall mean an individual or Legal Entity * exercising permissions granted by this License. * * "Source" form shall mean the preferred form for making modifications, * including but not limited to software source code, documentation * source, and configuration files. * * "Object" form shall mean any form resulting from mechanical * transformation or translation of a Source form, including but * not limited to compiled object code, generated documentation, * and conversions to other media types. * * "Work" shall mean the work of authorship, whether in Source or * Object form, made available under the License, as indicated by a * copyright notice that is included in or attached to the work * (an example is provided in the Appendix below). * * "Derivative Works" shall mean any work, whether in Source or Object * form, that is based on (or derived from) the Work and for which the * editorial revisions, annotations, elaborations, or other modifications * represent, as a whole, an original work of authorship. For the purposes * of this License, Derivative Works shall not include works that remain * separable from, or merely link (or bind by name) to the interfaces of, * the Work and Derivative Works thereof. * * "Contribution" shall mean any work of authorship, including * the original version of the Work and any modifications or additions * to that Work or Derivative Works thereof, that is intentionally * submitted to Licensor for inclusion in the Work by the copyright owner * or by an individual or Legal Entity authorized to submit on behalf of * the copyright owner. For the purposes of this definition, "submitted" * means any form of electronic, verbal, or written communication sent * to the Licensor or its representatives, including but not limited to * communication on electronic mailing lists, source code control systems, * and issue tracking systems that are managed by, or on behalf of, the * Licensor for the purpose of discussing and improving the Work, but * excluding communication that is conspicuously marked or otherwise * designated in writing by the copyright owner as "Not a Contribution." * * "Contributor" shall mean Licensor and any individual or Legal Entity * on behalf of whom a Contribution has been received by Licensor and * subsequently incorporated within the Work. * * 2. Grant of Copyright License. Subject to the terms and conditions of * this License, each Contributor hereby grants to You a perpetual, * worldwide, non-exclusive, no-charge, royalty-free, irrevocable * copyright license to reproduce, prepare Derivative Works of, * publicly display, publicly perform, sublicense, and distribute the * Work and such Derivative Works in Source or Object form. * * 3. Grant of Patent License. Subject to the terms and conditions of * this License, each Contributor hereby grants to You a perpetual, * worldwide, non-exclusive, no-charge, royalty-free, irrevocable * (except as stated in this section) patent license to make, have made, * use, offer to sell, sell, import, and otherwise transfer the Work, * where such license applies only to those patent claims licensable * by such Contributor that are necessarily infringed by their * Contribution(s) alone or by combination of their Contribution(s) * with the Work to which such Contribution(s) was submitted. If You * institute patent litigation against any entity (including a * cross-claim or counterclaim in a lawsuit) alleging that the Work * or a Contribution incorporated within the Work constitutes direct * or contributory patent infringement, then any patent licenses * granted to You under this License for that Work shall terminate * as of the date such litigation is filed. * * 4. Redistribution. You may reproduce and distribute copies of the * Work or Derivative Works thereof in any medium, with or without * modifications, and in Source or Object form, provided that You * meet the following conditions: * * (a) You must give any other recipients of the Work or * Derivative Works a copy of this License; and * * (b) You must cause any modified files to carry prominent notices * stating that You changed the files; and * * (c) You must retain, in the Source form of any Derivative Works * that You distribute, all copyright, patent, trademark, and * attribution notices from the Source form of the Work, * excluding those notices that do not pertain to any part of * the Derivative Works; and * * (d) If the Work includes a "NOTICE" text file as part of its * distribution, then any Derivative Works that You distribute must * include a readable copy of the attribution notices contained * within such NOTICE file, excluding those notices that do not * pertain to any part of the Derivative Works, in at least one * of the following places: within a NOTICE text file distributed * as part of the Derivative Works; within the Source form or * documentation, if provided along with the Derivative Works; or, * within a display generated by the Derivative Works, if and * wherever such third-party notices normally appear. The contents * of the NOTICE file are for informational purposes only and * do not modify the License. You may add Your own attribution * notices within Derivative Works that You distribute, alongside * or as an addendum to the NOTICE text from the Work, provided * that such additional attribution notices cannot be construed * as modifying the License. * * You may add Your own copyright statement to Your modifications and * may provide additional or different license terms and conditions * for use, reproduction, or distribution of Your modifications, or * for any such Derivative Works as a whole, provided Your use, * reproduction, and distribution of the Work otherwise complies with * the conditions stated in this License. * * 5. Submission of Contributions. Unless You explicitly state otherwise, * any Contribution intentionally submitted for inclusion in the Work * by You to the Licensor shall be under the terms and conditions of * this License, without any additional terms or conditions. * Notwithstanding the above, nothing herein shall supersede or modify * the terms of any separate license agreement you may have executed * with Licensor regarding such Contributions. * * 6. Trademarks. This License does not grant permission to use the trade * names, trademarks, service marks, or product names of the Licensor, * except as required for reasonable and customary use in describing the * origin of the Work and reproducing the content of the NOTICE file. * * 7. Disclaimer of Warranty. Unless required by applicable law or * agreed to in writing, Licensor provides the Work (and each * Contributor provides its Contributions) on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied, including, without limitation, any warranties or conditions * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A * PARTICULAR PURPOSE. You are solely responsible for determining the * appropriateness of using or redistributing the Work and assume any * risks associated with Your exercise of permissions under this License. * * 8. Limitation of Liability. In no event and under no legal theory, * whether in tort (including negligence), contract, or otherwise, * unless required by applicable law (such as deliberate and grossly * negligent acts) or agreed to in writing, shall any Contributor be * liable to You for damages, including any direct, indirect, special, * incidental, or consequential damages of any character arising as a * result of this License or out of the use or inability to use the * Work (including but not limited to damages for loss of goodwill, * work stoppage, computer failure or malfunction, or any and all * other commercial damages or losses), even if such Contributor * has been advised of the possibility of such damages. * * 9. Accepting Warranty or Additional Liability. While redistributing * the Work or Derivative Works thereof, You may choose to offer, * and charge a fee for, acceptance of support, warranty, indemnity, * or other liability obligations and/or rights consistent with this * License. However, in accepting such obligations, You may act only * on Your own behalf and on Your sole responsibility, not on behalf * of any other Contributor, and only if You agree to indemnify, * defend, and hold each Contributor harmless for any liability * incurred by, or claims asserted against, such Contributor by reason * of your accepting any such warranty or additional liability. * * END OF TERMS AND CONDITIONS * * APPENDIX: How to apply the Apache License to your work. * * To apply the Apache License to your work, attach the following * boilerplate notice, with the fields enclosed by brackets "[]" * replaced with your own identifying information. (Don't include * the brackets!) The text should be enclosed in the appropriate * comment syntax for the file format. We also recommend that a * file or class name and description of purpose be included on the * same "printed page" as the copyright notice for easier * identification within third-party archives. * * Copyright 2016 Alibaba Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * * */ package android.taobao.atlas.framework; import android.app.PreVerifier; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.os.Environment; import android.taobao.atlas.bundleInfo.AtlasBundleInfoManager; import android.taobao.atlas.bundleInfo.BundleListing; import android.taobao.atlas.framework.bundlestorage.BundleArchive; import android.taobao.atlas.runtime.ClassNotFoundInterceptorCallback; import android.taobao.atlas.runtime.InstrumentationHook; import android.taobao.atlas.runtime.LowDiskException; import android.taobao.atlas.runtime.RuntimeVariables; import android.taobao.atlas.util.*; import android.taobao.atlas.util.AtlasFileLock; import android.taobao.atlas.versionInfo.BaselineInfoManager; import android.text.TextUtils; import android.util.Log; import org.osgi.framework.Bundle; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleException; import org.osgi.framework.BundleListener; import org.osgi.framework.Constants; import org.osgi.framework.FrameworkEvent; import org.osgi.framework.FrameworkListener; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; public final class Framework { public final static String DEPRECATED_MARK = "deprecated"; public final static String UPDATED_MARK = "markUpdated"; /** * Version displayed upon startup and returned by System Bundle */ static final String FRAMEWORK_VERSION = "0.9.0"; static Map<String,Integer> restoreFailedMap = new HashMap<String,Integer>(); static String containerVersion = ""; private static ClassNotFoundInterceptorCallback classNotFoundCallback; public static String ATLAS_DEBUG_DIRECTORY; // the runtime args /** * framework basedir. */ private static String BASEDIR; /** * bundle location. */ private static String BUNDLE_LOCATION; /** * the location where the storage resides. */ public static final String STORAGE_LOCATION; /** * classloader buffer size. */ static int CLASSLOADER_BUFFER_SIZE; /** * log level. */ static int LOG_LEVEL; /** * debug outputs from bundles ? */ static boolean DEBUG_BUNDLES; /** * debug outputs from class loading ? */ static boolean DEBUG_CLASSLOADING; /** * location -> bundle. */ static Map<String, Bundle> bundles = new ConcurrentHashMap<String, Bundle>(); /** * bundle listeners. */ static List<BundleListener> bundleListeners = new ArrayList<BundleListener>(); /** * synchronous bundle listeners. */ static List<BundleListener> syncBundleListeners = new ArrayList<BundleListener>(); /** * framework listeners. */ static List<FrameworkListener> frameworkListeners = new ArrayList<FrameworkListener>(); /** * system ClassLoader */ static ClassLoader systemClassLoader; // the system bundle /** * system bundle. */ static SystemBundle systemBundle; // the fields /** * properties. */ static Properties properties; /** * the framework start level. */ static int startlevel = 0; /** * the dir name of WAL */ static List<String> writeAheads = new ArrayList<String>(); /** * the initial startlevel for installed bundles. */ static int initStartlevel = 1; static Map<String,String> mMapForComAndBundles = new HashMap<String,String>(); /** * a hook in delegateClassLoader's find class to trigger whether install/dexopt bundle when class not find happend */ public static boolean needRestart = false; static String currentProcessName; // constants /** * the admin permission. */ private static boolean bundleUpdated = false; static { File fileDir = RuntimeVariables.androidApplication.getFilesDir(); if (fileDir == null || !fileDir.exists()) { fileDir = RuntimeVariables.androidApplication.getFilesDir(); } try { ATLAS_DEBUG_DIRECTORY = RuntimeVariables.androidApplication.getExternalFilesDir("atlas-debug").getAbsolutePath(); } catch (Exception e) { ATLAS_DEBUG_DIRECTORY = "/sdcard/Android/data/" + RuntimeVariables.androidApplication.getPackageName() + "/files/atlas-debug"; } BASEDIR = fileDir.getAbsolutePath(); STORAGE_LOCATION = BASEDIR + File.separatorChar + "storage" + File.separatorChar; } /** * Hide defautlt constructor */ private Framework() { if(Boolean.FALSE.booleanValue()){ String.valueOf(PreVerifier.class); } } /* * startup and shutdown related methods */ /** * launch the framework. * * @throws Throwable */ static void startup(Properties props) throws BundleException { // Log.e("Framwork","patch success!"); properties = props == null ? new Properties() : props; AtlasBundleInfoManager.instance().getBundleInfo(); startup(); } /** * launch the framework. * * @throws Throwable */ static void startup() throws BundleException { long time = System.currentTimeMillis(); initialize(); launch(); // if profile set, try to restart the profile boolean init = getProperty("osgi.init", true); // some non-main process, major dex is enough if (needRestart) { init = true; } if (init) { resetStorage(); } else if (RuntimeVariables.getProcessName(RuntimeVariables.androidApplication).equals(RuntimeVariables.androidApplication.getPackageName())) { restoreProfile(); } if(RuntimeVariables.shouldSyncUpdateInThisProcess()) { BaselineInfoManager.instance().checkUpdateBundles(STORAGE_LOCATION); } notifyFrameworkListeners(0 /* STARTING */, systemBundle, null); // save the metadata if (init) { try { storeProfile(); }catch(Exception e){ throw new RuntimeException("storeProfile failed", e); } } final long timediff = System.currentTimeMillis() - time; systemBundle.state = Bundle.ACTIVE; try { notifyFrameworkListeners(FrameworkEvent.STARTED, systemBundle, null); } catch (Exception e) { throw new RuntimeException("notifyFrameworkListeners failed", e); } } public static ClassLoader getSystemClassLoader() { return systemClassLoader; } public static List<Bundle> getBundles() { final List<Bundle> res = new ArrayList<Bundle>(bundles.size()); synchronized (bundles) { res.addAll(bundles.values()); } return res; } public synchronized static Bundle getBundle(String location) { if (location == null){ return null; } return bundles.get(location); } public static Bundle getBundle(long id) { return null; } private static void initialize() { BUNDLE_LOCATION = properties.getProperty("android.taobao.atlas.jars", "file:" + BASEDIR); CLASSLOADER_BUFFER_SIZE = getProperty("android.taobao.atlas.classloader.buffersize", 2048); LOG_LEVEL = getProperty("android.taobao.atlas.log.level", Log.ERROR); DEBUG_BUNDLES = getProperty("android.taobao.atlas.debug.bundles", false); DEBUG_CLASSLOADING = getProperty("android.taobao.atlas.debug.classloading", false); if (getProperty("android.taobao.atlas.debug", false)) { LOG_LEVEL = Log.DEBUG; DEBUG_BUNDLES = true; DEBUG_CLASSLOADING = true; } final String sv = System.getProperty("java.specification.version"); final String sn = System.getProperty("java.specification.name"); properties.put("org.osgi.framework.executionenvironment", sn + "/" + sv); // set framework properties Object obj; properties.put(Constants.FRAMEWORK_OS_NAME, (obj = System.getProperty("os.name")) != null ? obj : "undefined"); properties.put(Constants.FRAMEWORK_OS_VERSION, (obj = System.getProperty("os.version")) != null ? obj : "undefined"); properties.put(Constants.FRAMEWORK_PROCESSOR, (obj = System.getProperty("os.arch")) != null ? obj : "undefined"); properties.put(Constants.FRAMEWORK_VERSION, FRAMEWORK_VERSION); properties.put(Constants.FRAMEWORK_VENDOR, "Atlas"); final String lang = java.util.Locale.getDefault().getLanguage(); properties.put(Constants.FRAMEWORK_LANGUAGE, lang != null ? lang : "en"); } /** * create the setup with the properties and the internal framework flags. */ private static void launch() { // create the system bundle systemBundle = new SystemBundle(); systemBundle.state = Bundle.STARTING; } /** * get a boolean property. * * @param key the key. * @param defaultVal the default. * @return the value. */ public static boolean getProperty(final String key, final boolean defaultVal) { if (properties == null) { return defaultVal; } final String val = (String) properties.get(key); return val != null ? Boolean.valueOf(val).booleanValue() : defaultVal; } /** * get an int property. * * @param key the key. * @param defaultVal the default. * @return the value. */ public static int getProperty(final String key, final int defaultVal) { if (properties == null) { return defaultVal; } final String val = (String) properties.get(key); return val != null ? Integer.parseInt(val) : defaultVal; } /** * get an String property. * * @param key the key. * @return the value. */ public static String getProperty(final String key) { if (properties == null) { return null; } return (String) properties.get(key); } /** * get an String property. * * @param key * @param defaultString * @return */ public static String getProperty(final String key, final String defaultString) { if (properties == null) { return defaultString; } return (String) properties.get(key); } /** * write a warning or throw an Exception * * @param message * @throws BundleException */ protected static void warning(String message) throws RuntimeException { if (getProperty("android.taobao.atlas.strictStartup", false)) { throw new RuntimeException(message); } System.err.println("WARNING: " + message); } /** * store the profile. */ private static void storeProfile() { // final BundleImpl[] bundleArray = (BundleImpl[]) getBundles().toArray(new BundleImpl[bundles.size()]); // for (int i = 0; i < bundleArray.length; i++) { // bundleArray[i].updateMetadata(); // } storeMetadata(); } /** * store the framework metadata. */ static void storeMetadata() { File file = null; try { file = new File(STORAGE_LOCATION, "meta"); final DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); out.writeInt(startlevel); String str = StringUtils.join(writeAheads.toArray(), ","); out.writeUTF(str != null ? str : ""); out.flush(); out.close(); } catch (IOException ioe) { // AtlasMonitor.getInstance().trace(AtlasMonitor.BUNDLE_INSTALL_FAIL, "System", AtlasMonitor.UPDATE_META_FAILED_MSG, // FileUtils.getDataAvailableSpace()); Log.e("Framework","Could not save meta data.", ioe); } } public static String getCurProcessName() { return RuntimeVariables.getProcessName(RuntimeVariables.androidApplication); } /** * restore a profile. * * @return the startlevel or -1 if the profile could not be restored. */ private static int restoreProfile() { try { final File file = new File(STORAGE_LOCATION, "meta"); if (!file.exists()) { return -1; } final DataInputStream in = new DataInputStream(new FileInputStream(file)); final int targetStartlevel = in.readInt(); String[] arr = StringUtils.split(in.readUTF(), ","); if (arr != null) { writeAheads.addAll(Arrays.asList(arr)); } try { in.close(); } catch (Throwable e) { } final File storageDir = new File(STORAGE_LOCATION); // merge writeAheads to storage MergeWirteAheads(storageDir); return targetStartlevel; } catch (Exception ioe) { // throw new RuntimeException(ioe); return -1; } } private static void resetStorage() { File storage = new File(STORAGE_LOCATION); // Try to remove the directory by three times. int count = 3; while (count-- > 0) { if (storage.exists()) { try { deleteDirectory(storage); } catch (Exception e) { if (count == 1) { // AtlasMonitor.getInstance().trace(AtlasMonitor.DELETE_STORAGE_FAIL, // "System", AtlasMonitor.DELETE_STORAGE_FAILED_MSG, FileUtils.getDataAvailableSpace()); throw new RuntimeException("deleteDirectory failed", e); } } } else { break; } } // Still not removed yet, throw exception if (storage.exists()) { // AtlasMonitor.getInstance().trace(AtlasMonitor.DELETE_STORAGE_FAIL, // "System", AtlasMonitor.DELETE_STORAGE_FAILED_MSG, FileUtils.getDataAvailableSpace()); throw new RuntimeException("deleteDirectory failed"); } try { storage.mkdirs(); } catch (Throwable e) { } if (!storage.exists()) { throw new RuntimeException("mkdirs failed"); } } private static void MergeWirteAheads(final File storageDir) { try{ final File walsDir = new File(STORAGE_LOCATION, "wal"); String process = RuntimeVariables.getProcessName(RuntimeVariables.androidApplication); Log.d("Framework","restoreProfile in process "+process); if(process!=null) { mergeWalsDir(walsDir, storageDir); } }catch(Throwable e){ if (android.os.Build.MODEL != null){ throw new RuntimeException(e); } } } private static void mergeWalsDir(File walsDir, File storageDir) { if (writeAheads != null && writeAheads.size() > 0) { for (int i = 0; i < writeAheads.size(); i++) { if (writeAheads.get(i) == null) { continue; } File walDir = new File(walsDir, writeAheads.get(i)); try { if (walDir != null && walDir.exists()) { // merge wal dir to storage final File[] walBundleDirs = walDir.listFiles(); if ((walBundleDirs != null) && (walBundleDirs.length > 0)) { for (File walBundleDir : walBundleDirs) { if (walBundleDir.isDirectory()) { File bundleDir = new File(storageDir, walBundleDir.getName()); // if (bundleDir.exists()) { // deleteDirectory(bundleDir); // } File newRevDir = new File(walBundleDir,"version.1"); if(!newRevDir.exists()){ return; } File[] revisions = bundleDir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { return filename.startsWith(BundleArchive.REVISION_DIRECTORY); } }); int targetREV = 1; List<String> useless = new ArrayList<>(); File latestRevision = null; String targetVersion = BundleArchive.REVISION_DIRECTORY+".1"; int rev; if(revisions!=null && revisions.length>0){ for(File revision : revisions){ if((rev=Integer.parseInt(revision.getName().substring(8)))>= targetREV){ targetREV = rev; if(!new File(revision,BundleArchive.DEPRECATED_MARK).exists()) { if (latestRevision != null) { useless.add(latestRevision.getName()); } latestRevision = revision; } }else{ useless.add(revision.getName()); } if(new File(revision,BundleArchive.DEPRECATED_MARK).exists()){ useless.add(revision.getName()); } } if(!BaselineInfoManager.instance().isCachePreVersion()){ useless.add(latestRevision.getName()); } targetVersion = BundleArchive.REVISION_DIRECTORY+"."+(++targetREV); } File targetDir = new File(bundleDir,targetVersion); targetDir.mkdirs(); File meta = new File(bundleDir,"meta"); if(meta.exists()){ meta.delete(); } new File(walBundleDir,"meta").renameTo(meta); // move bundle to storage boolean result = newRevDir.renameTo(targetDir); if(!result || !targetDir.exists()){ targetDir.mkdirs(); result = newRevDir.renameTo(targetDir); new File(walBundleDir,"meta").renameTo(meta); if(!result || !targetDir.exists() || !meta.exists()){ BaselineInfoManager.instance().rollbackHardly(); android.os.Process.killProcess(android.os.Process.myPid()); } }else{ //remove old bundles for(String uselessDir : useless){ File uselessFile = new File(bundleDir,uselessDir); if(uselessFile.exists()){ deleteDirectory(uselessFile); } if(uselessFile.exists()){ new File(uselessFile,BundleArchive.DEPRECATED_MARK).createNewFile(); } } } } } } } writeAheads.set(i, null); } catch (Exception e) { Log.e("Framework","Error while merge wal dir", e); } } } if (walsDir.exists()) { deleteDirectory(walsDir); } } /** * delete a directory with all subdirs. * * @param path the directory. */ public static void deleteDirectory(final File path) { final File[] files = path.listFiles(); if (files == null){ return; } for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) { deleteDirectory(files[i]); } else { files[i].delete(); } } path.delete(); } /* * framework operations */ /** * install a bundle. if location starts with asset: then fetch file from the assets folder. * * @param location the bundle location. * @return a Bundle object. * @throws BundleException if the installation failed. */ static Bundle installNewBundle(final String location) throws BundleException { try{ final String location2 = location.indexOf(":") > -1 ? location : BUNDLE_LOCATION + File.separatorChar + location; return installNewBundle(location2, new URL(location2).openConnection().getInputStream()); } catch (IOException e) { throw new BundleException("Cannot retrieve bundle from " + location, e); } } /** * install a bundle from input stream. * * @param location the bundle location. * @param in the input stream. * @return a Bundle object. * @throws BundleException if the installation failed. */ static BundleImpl installNewBundle(final String location, final InputStream in)throws BundleException { File bundleDir = null; try{ BundleLock.WriteLock(location); /* * <specs page="58">Every bundle is uniquely identified by its location string. If an installed bundle is using * the specified location, the installBundle method must return the Bundle object for that installed bundle and * not install a new bundle.</specs> */ bundleDir = new File(STORAGE_LOCATION, location); if(!bundleDir.exists()){ bundleDir.mkdirs(); } AtlasFileLock.getInstance().LockExclusive(bundleDir); final BundleImpl cached; if ((cached = (BundleImpl) getBundle(location)) != null) { return cached; } Log.e("BundleInstaller","real install " + location); BundleImpl bundle = null; BundleListing.BundleInfo info = AtlasBundleInfoManager.instance().getBundleInfo(location); String version = info!=null ? info.getVersion() : "-1"; bundle = new BundleImpl(bundleDir, location, new BundleContext(), in, null, version,true,-1); // storeMetadata(); return bundle; } catch (IOException e) { BundleException e1 = new BundleException("Failed to install bundle." + FileUtils.getAvailableDisk(), e); if (bundleDir != null) Framework.deleteDirectory(bundleDir); if (FileUtils.getUsableSpace(Environment.getDataDirectory()) < LowDiskException.thredshold) { throw new LowDiskException(FileUtils.getAvailableDisk(), e); } throw new BundleException("Failed to install bundle.", e); } catch (BundleException e) { BundleException e1 = new BundleException("Failed to install bundle." + FileUtils.getAvailableDisk(), e); if (bundleDir != null) Framework.deleteDirectory(bundleDir); throw e1; } finally { BundleLock.WriteUnLock(location); if (bundleDir != null) { AtlasFileLock.getInstance().unLock(bundleDir); } } } /** * install a bundle from input file, the file will move to bundle storage directory. * * @param location the bundle location. * @param file the input file. * @return a Bundle object. * @throws BundleException if the installation failed. * @throws */ static BundleImpl installNewBundle(final String location, final File file) throws BundleException { File bundleDir = null; try { bundleDir = new File(STORAGE_LOCATION, location); if(!bundleDir.exists()){ bundleDir.mkdirs(); } BundleLock.WriteLock(location); AtlasFileLock.getInstance().LockExclusive(bundleDir); /* * <specs page="58">Every bundle is uniquely identified by its location string. If an installed bundle is using * the specified location, the installBundle method must return the Bundle object for that installed bundle and * not install a new bundle.</specs> */ final BundleImpl cached; if ((cached = (BundleImpl) getBundle(location)) != null) { return cached; } Log.e("BundleInstaller","real install " + location); BundleImpl bundle = null; BundleListing.BundleInfo info = AtlasBundleInfoManager.instance().getBundleInfo(location); String version = info!=null ? info.getVersion() : "-1"; bundle = new BundleImpl(bundleDir, location, new BundleContext(), null, file,version,true,-1); // storeMetadata(); return bundle; } catch (IOException e) { BundleException e1 = new BundleException("Failed to install bundle." + FileUtils.getAvailableDisk(), e); if (bundleDir != null) Framework.deleteDirectory(bundleDir); if (FileUtils.getUsableSpace(Environment.getDataDirectory()) < LowDiskException.thredshold) { throw new LowDiskException(FileUtils.getAvailableDisk(), e); } throw new BundleException("Failed to install bundle.", e); } catch (BundleException e) { BundleException e1 = new BundleException("Failed to install bundle." + FileUtils.getAvailableDisk(), e); if (bundleDir != null) Framework.deleteDirectory(bundleDir); throw e1; } finally { BundleLock.WriteUnLock(location); if (bundleDir != null) { AtlasFileLock.getInstance().unLock(bundleDir); } } } static boolean restoreBundle(String[] locations) { try { for (String location : locations) { if (isKernalBundle(location)) { final File bundleDir = new File(STORAGE_LOCATION, "com.taobao.maindex"); if (!bundleDir.exists()) { return false; } else { Class KernalBundleClass = RuntimeVariables.getRawClassLoader().loadClass("android.taobao.atlas.startup.patch.KernalBundle"); Method downgradeRevision = KernalBundleClass.getDeclaredMethod("downgradeRevision",File.class,boolean.class); boolean success = (Boolean)downgradeRevision.invoke(KernalBundleClass,bundleDir,false); if (!success) { return false; } } } else { final File bundleDir = new File(STORAGE_LOCATION, location); if (!bundleDir.exists()) { return false; } else { if (!BundleArchive.downgradeRevision(location, bundleDir, false)) { return false; } } } } } catch (Exception e) { return false; } return true; } /** * boolean lockSuccess = false; try { lockSuccess = BundleLock.ReadLock(location); if (location == null){ return null; } Bundle bundle = bundles.get(location); if(bundle==null) { bundle = restoreFromExistedBundle(location); } return bundle; }finally { if(lockSuccess) { try { BundleLock.ReadUnLock(location); }catch(Throwable e){} } } * @param location * @return */ public static BundleImpl restoreFromExistedBundle(final String location) { boolean lockSuccess = false; File bundleDir = new File(STORAGE_LOCATION, location); BundleImpl bundle = null; // just restore if(bundleDir.exists() && new File(bundleDir,"meta").exists()) { try { lockSuccess = BundleLock.ReadLock(location); AtlasFileLock.getInstance().LockExclusive(bundleDir); BundleContext impl = new BundleContext(); if(BaselineInfoManager.instance().dexPatchVersion()>0 && BaselineInfoManager.instance().isDexPatched(location)) { impl.dexPatchVersion = BaselineInfoManager.instance().dexPatchVersion(); bundle = new BundleImpl(bundleDir, impl); if (bundle != null) { bundle.optDexFile(); } }else{ String[] versions = bundleDir.list(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { return filename.startsWith(BundleArchive.REVISION_DIRECTORY); } }); if(versions!=null && versions.length>0){ bundle = new BundleImpl(bundleDir, impl); if (bundle != null) { bundle.optDexFile(); } } } } catch (Exception e) { if (e instanceof BundleArchive.MisMatchException) { if (bundleDir.exists()) { bundle = null; } } // AtlasMonitor.getInstance().trace(AtlasMonitor.BUNDLE_INSTALL_FAIL, // location, AtlasMonitor.RESTORED_FAILED_MSG, FileUtils.getDataAvailableSpace()); Log.e("Framework","restore bundle failed" + location, e); }finally { if(lockSuccess) { try { BundleLock.ReadUnLock(location); }catch(Throwable e){} } AtlasFileLock.getInstance().unLock(bundleDir); } } return bundle; } static void installOrUpdate(final String[] locations, final File[] files, String[] newBundleVersions,long dexPatchVersion) throws BundleException { if (locations == null || files == null || locations.length != files.length) { throw new IllegalArgumentException("locations and files must not be null and must be same length"); } String writeAhead = String.valueOf(System.currentTimeMillis()); File walsDir = new File(STORAGE_LOCATION, "wal"); File walDir = new File(walsDir, writeAhead); walDir.mkdirs(); Class KernalBundleClass = null; Object kernalBundle = null; for (int i = 0; i < locations.length; i++) { if (locations[i] == null || files[i] == null) { continue; } File bundleDir = null; try { BundleLock.WriteLock(locations[i]); if (isKernalBundle(locations[i])) { KernalBundleClass = RuntimeVariables.getRawClassLoader().loadClass("android.taobao.atlas.startup.patch.KernalBundle"); kernalBundle = KernalBundleClass.getDeclaredField("kernalBundle").get(KernalBundleClass); if (kernalBundle != null) { Method updateMethod = KernalBundleClass.getMethod("update",File.class,String.class,long.class); updateMethod.setAccessible(true); updateMethod.invoke(kernalBundle,files[i],Framework.containerVersion,dexPatchVersion); // bundle.update(); } else { bundleDir = new File(STORAGE_LOCATION, "com.taobao.maindex"); if (!bundleDir.exists()){ bundleDir.mkdirs(); } AtlasFileLock.getInstance().LockExclusive(bundleDir); Constructor cons = KernalBundleClass.getDeclaredConstructor(File.class,File.class,String.class,long.class); cons.setAccessible(true); Object b = cons.newInstance(bundleDir, files[i],Framework.containerVersion,dexPatchVersion); // KernalBundle b = new KernalBundle(bundleDir, files[i],Framework.containerVersion,dexPatchVersion); if (b != null) { Method getRevisionDir = b.getClass().getDeclaredMethod("getRevisionDir"); File file = (File) getRevisionDir.invoke(b); FileUtils.createNewDirIfNotExist(file, UPDATED_MARK); } } } else { Bundle bundle = Framework.getBundle(locations[i]); if (bundle != null) { bundle.update(files[i],newBundleVersions[i],dexPatchVersion); } else { if(dexPatchVersion>0){ bundleDir = new File(STORAGE_LOCATION, locations[i]); }else { bundleDir = new File(walDir, locations[i]); } if (!bundleDir.exists()) { bundleDir.mkdirs(); } // Hold the storage file lock AtlasFileLock.getInstance().LockExclusive(bundleDir); BundleImpl b = new BundleImpl(bundleDir, locations[i], new BundleContext(), null, files[i],newBundleVersions[i], false,dexPatchVersion); if (b != null) { FileUtils.createNewDirIfNotExist(b.archive.getCurrentRevision().getRevisionDir(), UPDATED_MARK); } } } } catch (Exception e) { // AtlasMonitor.getInstance().trace(AtlasMonitor.BUNDLE_INSTALL_FAIL, // locations[i], AtlasMonitor.UPDATE_FAILED_MSG, FileUtils.getDataAvailableSpace()); /** * bundle 安装需要支持事务,失败时回滚已部署的bundle */ for (int x = 0; x <= i; x++) { if (isKernalBundle(locations[x])) { if (kernalBundle != null) { try { Method downgradeRevision = kernalBundle.getClass().getDeclaredMethod("downgradeRevision",File.class,boolean.class); downgradeRevision.setAccessible(true); downgradeRevision.invoke(KernalBundleClass,new File(STORAGE_LOCATION, "com.taobao.maindex"), true); // KernalBundle.downgradeRevision(new File(STORAGE_LOCATION, "com.taobao.maindex"), true); } catch (Throwable e2) { } } else { deleteDirectory(new File(STORAGE_LOCATION, "com.taobao.maindex")); } } else { try { BundleArchive.downgradeRevision(locations[x], new File(STORAGE_LOCATION, locations[x]), true); } catch (IOException e1) { e1.printStackTrace(); } deleteDirectory(walDir); if (walDir.exists()) { try { new File(walDir, DEPRECATED_MARK).createNewFile(); } catch (IOException e1) { e1.printStackTrace(); } } } } throw new BundleException("failed to installOrUpdate bundles ", e); } finally { if (bundleDir != null) { AtlasFileLock.getInstance().unLock(bundleDir); } BundleLock.WriteUnLock(locations[i]); } } bundleUpdated = true; WrapperUtil.appendLog("installedVersionWhenUpdated", Framework.containerVersion); WrapperUtil.appendLog("VersionWhenUpdated", WrapperUtil.getPackageInfo(RuntimeVariables.androidApplication).versionName); writeAheads.add(writeAhead); InstrumentationHook.notifyAppUpdated(); storeMetadata(); } static boolean isKernalBundle(String location) { if (TextUtils.isEmpty(location)) { return false; } return location.equals("com.taobao.maindex"); } /** * notify all bundle listeners. * * @param state the new state. * @param bundle the bundle. */ static void notifyBundleListeners(final int state, final Bundle bundle) { if (syncBundleListeners.isEmpty() && bundleListeners.isEmpty()) { return; } final BundleEvent event = new BundleEvent(state, bundle); // inform the synchrounous bundle listeners first ... final BundleListener[] syncs = (BundleListener[]) syncBundleListeners.toArray(new BundleListener[syncBundleListeners.size()]); for (int i = 0; i < syncs.length; i++) { syncs[i].bundleChanged(event); } if (bundleListeners.isEmpty()) { return; } final BundleListener[] asyncs = (BundleListener[]) bundleListeners.toArray(new BundleListener[bundleListeners.size()]); for (int i = 0; i < asyncs.length; i++) { asyncs[i].bundleChanged(event); } } public static void addFrameworkListener(FrameworkListener listener) { frameworkListeners.add(listener); } public static void removeFrameworkListener(FrameworkListener listener) { frameworkListeners.remove(listener); } static void addBundleListener(BundleListener listener) { bundleListeners.add(listener); } static void removeBundleListener(BundleListener listener) { bundleListeners.remove(listener); } /** * notify all framework listeners. * * @param state the new state. * @param bundle the bundle. * @param throwable a throwable. */ static void notifyFrameworkListeners(final int state, final Bundle bundle, final Throwable throwable) { if (frameworkListeners.isEmpty()) { return; } final FrameworkEvent event = new FrameworkEvent(state, bundle, throwable); final FrameworkListener[] listeners = (FrameworkListener[]) frameworkListeners.toArray(new FrameworkListener[frameworkListeners.size()]); for (int i = 0; i < listeners.length; i++) { final FrameworkListener listener = listeners[i]; listener.frameworkEvent(event); } } /** * clear all traces of a bundle. * * @param bundle the bundle. */ static void clearBundleTrace(final BundleImpl bundle) { // remove all registered listeners if (bundle.registeredFrameworkListeners != null) { frameworkListeners.removeAll(bundle.registeredFrameworkListeners); bundle.registeredFrameworkListeners = null; } if (bundle.registeredBundleListeners != null) { bundleListeners.removeAll(bundle.registeredBundleListeners); syncBundleListeners.removeAll(bundle.registeredBundleListeners); bundle.registeredBundleListeners = null; } } /** * add a value to a value list in a Map. * * @param map the map. * @param key the key. * @param value the value to be added to the list. */ @SuppressWarnings({"rawtypes", "unchecked"}) static void addValue(final Map map, final Object key, final Object value) { List values; if ((values = (List) map.get(key)) == null) { values = new ArrayList<Bundle>(); } values.add(value); map.put(key, values); } /* * inner classes */ /** * The systemBundle. */ private static final class SystemBundle implements Bundle { /** * the state */ int state; /** * the properties. */ private final Dictionary<String, String> props = new Hashtable<String, String>(); /** * create the system bundle instance. */ SystemBundle() { props.put(Constants.BUNDLE_NAME, Constants.SYSTEM_BUNDLE_LOCATION); props.put(Constants.BUNDLE_VERSION, FRAMEWORK_VERSION); props.put(Constants.BUNDLE_VENDOR, "Atlas"); } /** * get the bundle id. * * @return 0. * @category Bundle * @see org.osgi.framework.Bundle#getBundleId() */ public long getBundleId() { return 0; } /** * get the properties. * * @return the properties. * @category Bundle * @see org.osgi.framework.Bundle#getHeaders() */ public Dictionary<String, String> getHeaders() { return props; } /** * get the location. * * @return "System Bundle" * @category Bundle * @see org.osgi.framework.Bundle#getLocation() */ public String getLocation() { return Constants.SYSTEM_BUNDLE_LOCATION; } /** * get resources. * * @param name the name. * @return the URL or null. * @category Bundle * @see org.osgi.framework.Bundle#getResource(java.lang.String) */ public URL getResource(final String name) { return getClass().getResource(name); } /** * get the state. * * @return the state. * @category Bundle * @see org.osgi.framework.Bundle#getState() */ public int getState() { return state; } /** * check if some permissions are granted. * * @param permission the permissions. * @return true, if the permissions hold. * @category Bundle * @see org.osgi.framework.Bundle#hasPermission(java.lang.Object) */ public boolean hasPermission(final Object permission) { return true; } /** * start the system bundle. * * @throws BundleException never. * @category Bundle * @see org.osgi.framework.Bundle#start() */ public void start() throws BundleException { // this method has no effect } /** * stopping the system bundle means shutting down the framework. * * @throws BundleException never. * @category Bundle * @see org.osgi.framework.Bundle#stop() */ public void stop() throws BundleException { } /** * the system bundle cannot be uninstalled. * * @throws BundleException always. * @category Bundle * @see org.osgi.framework.Bundle#uninstall() */ public void uninstall() throws BundleException { throw new BundleException("Cannot uninstall the System Bundle"); } public void update(final File in,String version,long dexPatchVersion) throws BundleException { throw new BundleException("Cannot update the System Bundle"); } /** * get a string representation. * * @return the string representation. * @category Object * @see java.lang.Object#toString() */ public String toString() { return "SystemBundle"; } } public static ClassNotFoundInterceptorCallback getClassNotFoundCallback() { return classNotFoundCallback; } public static void setClassNotFoundCallback(ClassNotFoundInterceptorCallback classNotFoundCallback) { Framework.classNotFoundCallback = classNotFoundCallback; } /** * for debug not release */ static void checkInstallDebugBundle() { File debugDirectory = new File(ATLAS_DEBUG_DIRECTORY); if (debugDirectory.exists()) { File[] bundles = debugDirectory.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { if (filename.endsWith(".so")) { return true; } return false; } }); if (bundles != null) { String[] packageNames = new String[bundles.length]; String[] versions = new String[bundles.length]; for (int x = 0; x < bundles.length; x++) { versions[x] = "1.0.0"; if (bundles[x].getName().startsWith("kernal") || bundles[x].getName().contains("com_taobao_mainDex")) { //主dexbundle packageNames[x] = "com.taobao.maindex"; } else { //业务bundle PackageInfo info = RuntimeVariables.androidApplication.getPackageManager().getPackageArchiveInfo(bundles[x].getAbsolutePath(), 0); if (info != null) { packageNames[x] = info.applicationInfo.packageName; } else { String fileName = bundles[x].getName(); fileName = fileName.substring(3, fileName.length() - 3); packageNames[x] = fileName.replace("_", "."); } } } try { Atlas.getInstance().installOrUpdate(packageNames, bundles,versions,-1); Log.d("Framework", "patch success and delete file"); try { for (File installFile : bundles) { if (installFile.exists()) { installFile.delete(); } } } catch (Exception e) { } } catch (BundleException e) { e.printStackTrace(); } } } } /** * 存在隐患 storelocation后期被更改 */ public static File getInstalledBundle(String location,String bundleVersion) { try { if (new File(STORAGE_LOCATION + location).exists()) { BundleArchive archive = new BundleArchive(location, new File(STORAGE_LOCATION + location),0,0); String version = archive.getCurrentRevision().getVersion(); if(TextUtils.isEmpty(bundleVersion) || version.equals(bundleVersion)) { return archive.getArchiveFile(); }else{ return null; } } return null; } catch (IOException e) { e.printStackTrace(); return null; } } public static boolean isUpdated() { return bundleUpdated; } public static String getContainerVersion() { return containerVersion; } public static boolean isDeubgMode() { try { /** * enable patch debug if in debug mode */ final ApplicationInfo app_info = RuntimeVariables.androidApplication.getApplicationInfo(); boolean DEBUG = (app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; if (DEBUG) { return true; } } catch (final Exception e) { return false; } return false; } public static boolean shouldSyncUpdateInThisProcess(){ String processName = getCurProcessName(); if(processName!=null && (processName.equals(RuntimeVariables.androidApplication.getPackageName()) || processName.toLowerCase().contains(":safemode") )){ return true; }else{ return false; } } }