package org.bbs.felix; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; import org.bbs.felix.util.AndroidUtil; import org.bbs.felix.util.PackageParser; import org.bbs.felix.util.PackageParser.PackageInfoX; import org.bbs.felix.util.OsgiUtil; import org.bbs.osgi.activity.embed.EmbeddedActivityAgent; import org.knopflerfish.framework.FrameworkFactoryImpl; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleException; import org.osgi.framework.BundleListener; import org.osgi.framework.Constants; import org.osgi.framework.launch.Framework; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowManager; /** * * @see <a href="http://felix.apache.org/site/apache-felix-framework-and-google-android.html">felist-on-android</a> * @see {felix source}/main/src/main/java/org/apache/felix/main/Main.java * @author bangbang.song@gmail.com * */ public class FelixWrapper{ private static final String TAG = FelixWrapper.class.getSimpleName(); private static final String TMP_DIR = "tmp"; private static final String OSGI_BUNDLE_DIR = "osgi_bundle"; private static final String OSGI_BUNDLE_CACHE_DIR = "osgi_bundlecache"; private static final String ASSERT_PRELOAD_BUNDLE_DIR = "felix/preloadbundle"; private static final String ASSERT_AUTO_EXTRACT_DIR = "autoExtract"; private static final String BUNDLE_FILE_INSTALL_DIR = "fileInstall"; private static FelixWrapper sInstance; private Framework mFramework; private String mTmpDir; private String mCacheDir; private String mBundleDir; private String mFileInstallDir; private Context mContext; private FelixWrapper(Context context){ mContext = context; mTmpDir = context.getDir(TMP_DIR, Context.MODE_WORLD_WRITEABLE).toString(); mCacheDir = context.getDir(OSGI_BUNDLE_CACHE_DIR, Context.MODE_WORLD_WRITEABLE).toString(); mBundleDir = context.getDir(OSGI_BUNDLE_DIR, Context.MODE_WORLD_WRITEABLE).toString(); mFileInstallDir = context.getDir(BUNDLE_FILE_INSTALL_DIR, Context.MODE_WORLD_WRITEABLE).toString(); extractAssets(context); HashMap<String, String> configMap = new HashMap<String, String>(); configMap.put(Constants.FRAMEWORK_STORAGE, mCacheDir); configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, ANDROID_PACKAGES_FOR_EXPORT_EXTRA); Log.d(TAG, "ANDROID_PACKAGE : " + ANDROID_PACKAGE); Log.d(TAG, "ANDROID_PACKAGE_V4 : " + ANDROID_PACKAGE_V4); Log.d(TAG, "APP_PACKAGE : " + APP_PACKAGE); Log.d(TAG, "THIRD_PACKAGE : " + THIRD_PACKAGE); Log.d(TAG, Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA + ": " + ANDROID_PACKAGES_FOR_EXPORT_EXTRA); // felix config // configMap.put(FelixConstants.LOG_LEVEL_PROP, 4 + ""); System.setProperty("felix.fileinstall.dir", mFileInstallDir); System.setProperty("felix.fileinstall.log.level", 4 + ""); // knoperfish config // configMap.put("org.knopflerfish.framework.debug.classloader", "true"); // equix config mFramework = new FrameworkFactoryImpl().newFramework(configMap); Log.d(TAG, "init & start osgi." ); try { mFramework.init(); Bundle[] bundles = mFramework.getBundleContext().getBundles(); // for re-deploy bundle. for (Bundle b : bundles) { if (0 != b.getBundleId()) { b.uninstall(); } } mFramework.start(); } catch (BundleException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.d(TAG, "OSGi framework running, state: " + OsgiUtil.bundleState2Str(mFramework.getState())); registerListener(mFramework); extractPreloadBundle(context); installPreloadBundle(mFramework.getBundleContext(), mBundleDir); List<String> packageNames = new ArrayList<String>(); packageNames.add("com.youku.tv"); // packageNames.add("com.example.android.apis"); installAppAsBundle(mFramework.getBundleContext(), packageNames); Bundle[] bundles = mFramework.getBundleContext().getBundles(); for (Bundle b : bundles) { Log.d(TAG, "b: " + b.getBundleId() + " " + OsgiUtil.bundleState2Str(b.getState())); } } private void installAppAsBundle(BundleContext bundleContext, List<String> packageNames) { for (String p : packageNames) { String apk = AndroidUtil.getInstallApkPath(mContext, p); PackageInfo pInfo = mContext.getPackageManager().getPackageArchiveInfo(apk, PackageManager.GET_ACTIVITIES); Log.d(TAG, "pInfo: " + pInfo); PackageInfoX info = PackageParser.parseAPk(mContext, apk); Log.d(TAG, "mInfo: " + info); try { bundleContext.installBundle("file://" + apk).start(); } catch (BundleException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public Framework getFramework(){ return mFramework; } public static void registerListener(Framework f) { f.getBundleContext().addBundleListener(new BundleListener(){ @Override public void bundleChanged(BundleEvent e) { Log.d(TAG, "bundleChanged. e:" + e); }}); } private void extractAssets(Context context) { try { AssetManager assetsM = context.getResources().getAssets(); String[] files = assetsM.list(ASSERT_AUTO_EXTRACT_DIR); for (String aFile: files) { String assertFile = ASSERT_AUTO_EXTRACT_DIR + "/" + aFile; Log.d(TAG, "prepare bundle: " + aFile); InputStream in = assetsM.open(assertFile); String bFile = "/sdcard/autoextract/" + aFile; File f = new File(bFile); f.getParentFile().mkdirs(); OutputStream out = // context.openFileOutput(mBundleDir + "/" + aFile, 0); new FileOutputStream(bFile); int byteCount = 8096; byte[] buffer = new byte[byteCount]; int count = 0; while ((count = in.read(buffer, 0, byteCount)) != -1){ out.write(buffer, 0, count); } in.close(); out.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void extractPreloadBundle(Context context) { try { AssetManager assetsM = context.getResources().getAssets(); String[] files = assetsM.list(ASSERT_PRELOAD_BUNDLE_DIR); for (String aFile: files) { String assertFile = ASSERT_PRELOAD_BUNDLE_DIR + "/" + aFile; Log.d(TAG, "prepare bundle: " + aFile); InputStream in = assetsM.open(assertFile); String bFile = mBundleDir + "/" + aFile; if (aFile.endsWith("apk")) { bFile = bFile + ".jar"; } OutputStream out = // context.openFileOutput(mBundleDir + "/" + aFile, 0); new FileOutputStream(bFile); int byteCount = 8096; byte[] buffer = new byte[byteCount]; int count = 0; while ((count = in.read(buffer, 0, byteCount)) != -1){ out.write(buffer, 0, count); } in.close(); out.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void installPreloadBundle(BundleContext bundleContext, String bundleDir) { String[] files = new File(bundleDir).list(); for (String f : files ) { try { bundleContext.installBundle(new File("file://" + mBundleDir + "/" + f).getPath()).start(); } catch (BundleException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static FelixWrapper getInstance(Context context) { if (null == sInstance) { sInstance = new FelixWrapper(context); } return sInstance; } // copied from http://code.google.com/p/felix-on-android/ private static String ANDROID_PACKAGES_FOR_EXPORT_EXTRA = null; private static String THIRD_PACKAGE = // this no ';' or ',' , shit. ""; private static String APP_PACKAGE = "org.bbs.osgi.activity," + "org.bbs.osgi.activity.embed," + "org.bbs.felix.activity.ActivityAgent," + "org.bbs.felix.activity.bundlemanager," + "org.bbs.felixonandroid," ; private static String ANDROID_PACKAGE_V4 = "" ; //see http://developer.android.com/reference/android/package-summary.html // api level 21 private static String ANDROID_PACKAGE = "android, " + "android.app," + "android.animation," + "android.content," + "android.content.res," + "android.content.pm," + "android.database," + "android.database.sqlite," + "android.graphics, " + "android.graphics.drawable, " + "android.graphics.drawable.shapes," + "android.graphics.pdf," + "android.hardware, " + "android.hardware.camera2, " + "android.hardware.camera2.params, " + "android.hardware.display, " + "android.hardware.usb, " + "android.inputmethodservice, " + "android.location, " + "android.media, " + "android.media.audiofx, " + "android.media.browse, " + "android.media.effect, " + "android.media.projection, " + "android.media.session, " + "android.media.tv, " + "android.mtp, " + "android.net, " + "android.net.http, " + "android.net.nsd, " + "android.net.rtp, " + "android.net.sip, " + "android.net.wifi, " + "android.net.wifi.p2p, " + "android.net.wifi.psp.nsd, " + "android.nfc, " + "android.nfc.cardemulation, " + "android.nfc.tech, " + "android.opengl, " + "android.os, " + "android.os.storage, " + "android.preference, " + "android.print, " + "android.print.pdf, " + "android.printservice, " + "android.provider, " + "android.renderscript, " + "android.sax, " + "android.security, " + "android.service.dreams, " + "android.service.notification, " + "android.service.restriction, " + "android.service.textservice, " + "android.service.voice, " + "android.service.wallpaper, " + "android.speech, " + "android.speech.tss, " + "android.system, " + "android.tellecom, " + "android.telephony, " + "android.telephony.cdma, " + "android.telephony.gsm, " + "android.test, " + "android.test.mock, " + "android.test.suitebuilder, " + "android.test.suitebuilder.annotation, " + "android.text, " + "android.text.format, " + "android.text.method, " + "android.text.style, " + "android.text.util, " + "android.transition, " + "android.util, " + "android.view, " + "android.view.accessibility, " + "android.view.animation, " + "android.view.inputmethod, " + "android.view.textservice, " + "android.webkit, " + "android.widget, " + "com.android.internal.backup, " + "com.android.internal.os, " + "com.android.internal.statusbar, " + "com.android.internal.widget, " + "com.android.test.runner, " + "dalvik.annotation, " + "dalvik.bytecode, " + "dalvik.system, " + "javax.crypto, " + "javax.crypto.interfaces, " + "javax.crypto.spec, " + "javax.microedition.khronos.egl, " + "javax.microedition.khronos.opengles, " + "javax.net, " + "javax.net.ssl, " + "javax.security.auth, " + "javax.security.auth.callback, " + "javax.security.auth.login, " + "javax.security.auth.x500, " + "javax.security.cert, " + "javax.sql, " + "javax.xml, " + "javax.xml.datatype, " + "javax.xml.namespace, " + "javax.xml.parsers, " + "javax.xml.transfrom, " + "javax.xml.transfrom.dom, " + "javax.xml.transfrom.sax, " + "javax.xml.transfrom.stream, " + "javax.xml.validation, " + "javax.xml.xpath, " + "junit.framework, " + "junit.runner, " + "org.apache.http, " + "org.apache.http.auth, " + "org.apache.http.auth.params, " + "org.apache.http.client, " + "org.apache.http.client.entity, " + "org.apache.http.client.methods, " + "org.apache.http.client.params, " + "org.apache.http.client.protocol, " + "org.apache.http.client.utils, " + "org.apache.http.conn, " + "org.apache.http.conn.params, " + "org.apache.http.conn.routing, " + "org.apache.http.conn.scheme, " + "org.apache.http.conn.ssl, " + "org.apache.http.conn.util, " + "org.apache.http.cookie, " + "org.apache.http.cookie.params, " + "org.apache.http.entity, " + "org.apache.http.impl, " + "org.apache.http.impl.auth, " + "org.apache.http.impl.client, " + "org.apache.http.impl.conn, " + "org.apache.http.impl.conn.tsccm, " + "org.apache.http.impl.cookie, " + "org.apache.http.impl.entity, " + "org.apache.http.impl.io, " + "org.apache.http.io, " + "org.apache.http.message, " + "org.apache.http.params, " + "org.apache.http.protocol, " + "org.apache.http.util, " + "org.json, " + "org.w3c.dom, " + "org.w3c.dom.ls, " + "org.xml.sax, " + "org.xml.sax.extv, " + "org.xml.sax.helpers, " + "org.xmlpull.v1, " + "org.xmlpull.v1.sax2, " + "" ; static { ANDROID_PACKAGES_FOR_EXPORT_EXTRA += "," + APP_PACKAGE; ANDROID_PACKAGES_FOR_EXPORT_EXTRA += ANDROID_PACKAGE; // ANDROID_PACKAGES_FOR_EXPORT_EXTRA += ANDROID_PACKAGE_V4; ANDROID_PACKAGES_FOR_EXPORT_EXTRA += THIRD_PACKAGE; ANDROID_PACKAGES_FOR_EXPORT_EXTRA = ANDROID_PACKAGES_FOR_EXPORT_EXTRA.replace("\\s*,\\s*,\\s*", ""); ANDROID_PACKAGES_FOR_EXPORT_EXTRA = ANDROID_PACKAGES_FOR_EXPORT_EXTRA.replace("\\s*,\\s*$", ""); } }