From 82ea1301a5597506a37be7829c819251be4c4b39 Mon Sep 17 00:00:00 2001 From: Markus Jung Date: Tue, 16 Feb 2016 18:37:32 +0900 Subject: [PATCH] RC android: load bundle classes from library This change enables the resource container to load bundle classes from a .jar dynamically at runtime. It extends the startContainer method in order to return the status of a bundle, if it has been succesfully activated or not. Removed unnecessary white spaces. Change-Id: I158889e4366b89b0ac825d789d476d7330894926 Signed-off-by: Markus Jung Reviewed-on: https://gerrit.iotivity.org/gerrit/5011 Tested-by: jenkins-iotivity Reviewed-by: JungHo Kim --- resource/docs/javadocGen.sh | 7 +- .../android/resource-container/build.gradle | 4 + .../service/resourcecontainer/BundleActivator.java | 4 + .../BundleSoftSensorResource.java | 5 +- .../service/resourcecontainer/RcsBundleInfo.java | 51 ++++-- .../resourcecontainer/RcsResourceAttributes.java | 4 - .../resourcecontainer/RcsResourceContainer.java | 192 +++++++++++++-------- .../RcsResourceContainerBundleAPI.java | 3 +- .../service/resourcecontainer/ResourceConfig.java | 1 - .../src/main/jni/JniRcsResourceContainer.cpp | 3 +- .../android/AndroidBundle/app/build.gradle | 3 +- .../main/assets/lib/ResourceContainerConfig.xml | 66 ++++++- service/resource-container/include/RCSBundleInfo.h | 8 + .../src/ResourceContainerImpl.cpp | 5 + 14 files changed, 261 insertions(+), 95 deletions(-) diff --git a/resource/docs/javadocGen.sh b/resource/docs/javadocGen.sh index a18252a..3bd28a6 100755 --- a/resource/docs/javadocGen.sh +++ b/resource/docs/javadocGen.sh @@ -39,9 +39,12 @@ SIMULATOR_COMMON_PKG=org.oic.simulator SIMULATOR_CLIENT_PKG=org.oic.simulator.clientcontroller SIMULATOR_SERVER_PKG=org.oic.simulator.serviceprovider +RESOURCECONTAINER_PATH="../../service/resource-container/android/resource-container/src/main/java/" +RESOURCECONTAINER_PKG=org.iotivity.service.resourcecontainer + javadoc -public -splitindex -d ./Java_API -sourcepath \ - $BASE_PATH:$RE_PATH:$TM_PATH:$RH_PATH:$EASYSETUP_PATH:$SIMULATOR_PATH \ + $BASE_PATH:$RE_PATH:$TM_PATH:$RH_PATH:$EASYSETUP_PATH:$RESOURCECONTAINER_PATH:$SIMULATOR_PATH \ $BASE_PKG $RE_COMMON_PKG $RE_CLINET_PKG $RE_SERVER_PKG $TM_PKG $RH_PKG $EASYSETUP_PKG \ - $SIMULATOR_COMMON_PKG $SIMULATOR_CLIENT_PKG $SIMULATOR_SERVER_PKG + $SIMULATOR_COMMON_PKG $SIMULATOR_CLIENT_PKG $RESOURCECONTAINER_PKG $SIMULATOR_SERVER_PKG diff --git a/service/resource-container/android/resource-container/build.gradle b/service/resource-container/android/resource-container/build.gradle index ee1273e..d466538 100644 --- a/service/resource-container/android/resource-container/build.gradle +++ b/service/resource-container/android/resource-container/build.gradle @@ -20,6 +20,10 @@ apply plugin: 'com.android.library' +task jar(type: Jar) { + from fileTree(dir: 'build/intermediates/classes/release') +} + android { compileSdkVersion 21 buildToolsVersion "20.0.0" diff --git a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleActivator.java b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleActivator.java index 0e0a7e7..ebfd7ce 100644 --- a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleActivator.java +++ b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleActivator.java @@ -22,6 +22,10 @@ package org.iotivity.service.resourcecontainer; import android.content.Context; import java.util.List; +/** + * Every resource bundle has to provide a bundle activator that can be called + * by the resource container on bundle startup. + */ public abstract class BundleActivator { protected RcsResourceContainerBundleAPI bundleAPI; protected Context appContext; diff --git a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleSoftSensorResource.java b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleSoftSensorResource.java index 1102740..46eb9d3 100644 --- a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleSoftSensorResource.java +++ b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleSoftSensorResource.java @@ -27,7 +27,10 @@ import java.util.Vector; import android.content.Context; import android.util.Log; - +/** + * Every resource bundle has to provide a bundle activator that can be called + * by the resource container on bundle startup. + */ public abstract class BundleSoftSensorResource extends BundleResource { protected HashMap m_mapInputData; diff --git a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsBundleInfo.java b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsBundleInfo.java index 11e3112..5d2964f 100644 --- a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsBundleInfo.java +++ b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsBundleInfo.java @@ -34,6 +34,7 @@ public class RcsBundleInfo { private final String mActivatorName; private final String mLibraryPath; private final String mVersion; + private boolean mActivated; // not final since it might be modified for Android-specific bundles private RcsBundleInfo(String id, String path, String activatorName, String libraryPath, String version) { @@ -42,13 +43,24 @@ public class RcsBundleInfo { mActivatorName = activatorName; mLibraryPath = libraryPath; mVersion = version; + mActivated = false; + } + + private RcsBundleInfo(String id, String path, String activatorName, + String libraryPath, String version, boolean activated) { + mId = id; + mPath = path; + mActivatorName = activatorName; + mLibraryPath = libraryPath; + mVersion = version; + mActivated = activated; } /** * API for getting the Id of the bundle - * + * * @return string - Id of the bundle - * + * */ public String getID() { return mId; @@ -56,9 +68,9 @@ public class RcsBundleInfo { /** * API for getting the path of the bundle - * + * * @return path - path of the bundle - * + * */ public String getPath() { return mPath; @@ -66,9 +78,9 @@ public class RcsBundleInfo { /** * API for setting the Activator name for the bundle - * + * * @return string - Name of the activator - * + * */ public String getActivatorName() { return mActivatorName; @@ -76,9 +88,9 @@ public class RcsBundleInfo { /** * API for getting the library path for the bundle - * + * * @return string - Library path in string form - * + * */ public String getLibraryPath() { return mLibraryPath; @@ -86,12 +98,31 @@ public class RcsBundleInfo { /** * API for getting the version of the bundle - * + * * @return string - version of the bundle - * + * */ public String getVersion() { return mVersion; } + + /** + * Returns the current activation status of the bundle + * + * @return boolean - bundle has been successfully loaded and started + * + */ + public boolean isActivated() { + return mActivated; + } + /** + * Set the current activation status of the bundle + * + * @return boolean - bundle has been successfully loaded and started + * + */ + protected void setActivated(boolean activated) { + mActivated = activated; + } } diff --git a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceAttributes.java b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceAttributes.java index 453716d..87bb7e4 100644 --- a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceAttributes.java +++ b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceAttributes.java @@ -27,8 +27,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -//import org.iotivity.service.resourcecontainer.server.RcsLockedAttributes; - /** * * This class represents the attributes for a resource. @@ -43,7 +41,6 @@ public final class RcsResourceAttributes public RcsResourceAttributes() { } - public RcsResourceAttributes(RcsResourceAttributes attrs){ for (final String key : attrs.keySet()) { mCache.put(key, attrs.get(key)); @@ -202,5 +199,4 @@ public final class RcsResourceAttributes mCache.put(key, attrs.get(key)); } } - } diff --git a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainer.java b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainer.java index 3eaef68..9d06a86 100644 --- a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainer.java +++ b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainer.java @@ -26,6 +26,7 @@ package org.iotivity.service.resourcecontainer; import java.util.List; + import java.util.Map; import java.util.Enumeration; import android.util.Log; @@ -35,10 +36,15 @@ import java.util.Vector; import dalvik.system.DexFile; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import dalvik.system.PathClassLoader; +import java.net.URLClassLoader; import java.util.Hashtable; +import java.io.File; +import java.net.URL; + +import java.lang.reflect.InvocationTargetException; -// TODO null check for parameters /** * This class provides APIs for managing the container and bundles in the * container. @@ -46,6 +52,7 @@ import java.util.Hashtable; public class RcsResourceContainer implements RcsResourceContainerBundleAPI { private static final String TAG = RcsResourceContainer.class.getSimpleName(); + static { System.loadLibrary("gnustl_shared"); System.loadLibrary("oc_logger"); @@ -59,7 +66,7 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI { System.loadLibrary("rcs_container"); System.loadLibrary("resource_container_jni"); } - + private Context appContext; private native void nativeStartContainer(String configFile); @@ -114,45 +121,78 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI { * information. * */ - public void startContainer(String configFile) { + public List startContainer(String configFile) { nativeStartContainer(configFile); - Log.d(TAG, "startContainer"); + Log.d(TAG, "startContainer in Java"); List bundles = listBundles(); + Log.d(TAG, "startContainer. There are " + bundles.size() + " bundles."); for(RcsBundleInfo bundleInfo : bundles){ Log.d(TAG, "bundle-id: " + bundleInfo.getID() + ", " + bundleInfo.getPath()); - if(bundleInfo.getPath().endsWith(".apk")){ - String packageName = bundleInfo.getPath().replace(".apk", ""); - try{ - PackageManager packageManager = appContext.getPackageManager(); - ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0); - DexFile df = new DexFile(appInfo.sourceDir); - ClassLoader cl = appContext.getClassLoader(); - for (Enumeration iter = df.entries(); iter.hasMoreElements(); ) { - String classN = iter.nextElement(); - if (classN.contains(packageName)) { - Log.d(TAG,"Class: " + classN); - df.loadClass(classN, cl); - } - } - - String className = bundleInfo.getActivatorName(); - Log.d(TAG, "Loading activator: " + className); - Class activatorClass = df.loadClass(className, cl); - if(activatorClass!= null){ - BundleActivator activator = (BundleActivator) activatorClass. - getConstructor(RcsResourceContainerBundleAPI.class, Context.class). - newInstance(this, appContext); - activator.activateBundle(); - activators.put(bundleInfo.getID(), activator); - }else{ - Log.e(TAG, "Activator is null."); - } - } - catch(Exception e){ - Log.e(TAG, e.getMessage(), e); + if(bundleInfo.getPath().endsWith(".apk")){ // load classes from standalone application + startBundleFromStandaloneApp(bundleInfo); + }else if(bundleInfo.getPath().endsWith(".jar")){ // load classes from library + startBundleFromJar(bundleInfo); + } + } + return bundles; + } + + private void startBundleFromStandaloneApp(RcsBundleInfo bundleInfo){ + String packageName = bundleInfo.getPath().replace(".apk", ""); + try{ + PackageManager packageManager = appContext.getPackageManager(); + ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0); + DexFile df = new DexFile(appInfo.sourceDir); + ClassLoader cl = appContext.getClassLoader(); + for (Enumeration iter = df.entries(); iter.hasMoreElements(); ) { + String classN = iter.nextElement(); + if (classN.contains(packageName)) { + Log.d(TAG,"Class: " + classN); + df.loadClass(classN, cl); } - Log.d(TAG, "Have to register android bundle"); } + String className = bundleInfo.getActivatorName(); + Log.d(TAG, "Loading activator: " + className); + Class activatorClass = df.loadClass(className, cl); + activateBundle(activatorClass, bundleInfo); + } + catch(Exception e){ + Log.e(TAG, e.getMessage(), e); + } + Log.d(TAG, "Have to register android bundle"); + } + + private void startBundleFromJar(RcsBundleInfo bundleInfo){ + try{ + Log.e(TAG, "Loading from .jar file."); + + PathClassLoader classLoader = new PathClassLoader(bundleInfo.getPath(), + RcsResourceContainer.class.getClassLoader()); + + String className = bundleInfo.getActivatorName().replace('/', '.'); + Log.d(TAG, "Loading activator: " + className); + Class activatorClass = Class.forName(className, true, classLoader); + + activateBundle(activatorClass, bundleInfo); + } + catch(Exception e){ + Log.e(TAG, e.getMessage(), e); + } + Log.d(TAG, "Have to register android bundle"); + } + + private void activateBundle(Class activatorClass, RcsBundleInfo bundleInfo) throws + NoSuchMethodException, InstantiationException, IllegalAccessException, + InvocationTargetException{ + if(activatorClass!= null){ + BundleActivator activator = (BundleActivator) activatorClass. + getConstructor(RcsResourceContainerBundleAPI.class, Context.class). + newInstance(this, appContext); + activator.activateBundle(); + activators.put(bundleInfo.getID(), activator); + bundleInfo.setActivated(true); + }else{ + Log.e(TAG, "Activator is null."); } } @@ -227,49 +267,21 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI { public void startBundle(String bundleId) { Log.d(TAG, "startBundle"); List bundles = listBundles(); - - for(RcsBundleInfo bundleInfo : bundles){ + + for(RcsBundleInfo bundleInfo : bundles){ if(bundleInfo.getID().equals(bundleId) && bundleInfo.getLibraryPath().endsWith(".apk")){ Log.d(TAG, "Have to start android bundle"); Log.d(TAG, "bundle-id: " + bundleInfo.getID() + ", " + bundleInfo.getPath()); if(bundleInfo.getPath().endsWith(".apk")){ - String packageName = bundleInfo.getPath().replace(".apk", ""); - try{ - PackageManager packageManager = appContext.getPackageManager(); - ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0); - DexFile df = new DexFile(appInfo.sourceDir); - ClassLoader cl = appContext.getClassLoader(); - for (Enumeration iter = df.entries(); iter.hasMoreElements(); ) { - String classN = iter.nextElement(); - if (classN.contains(packageName)) { - Log.d(TAG,"Class: " + classN); - df.loadClass(classN, cl); - } - } - - String className = bundleInfo.getActivatorName(); - Log.d(TAG, "Loading activator: " + className); - Class activatorClass = df.loadClass(className, cl); - if(activatorClass!= null){ - BundleActivator activator = (BundleActivator) activatorClass. - getConstructor(RcsResourceContainerBundleAPI.class, - Context.class). - newInstance(this, appContext); - activator.activateBundle(); - }else{ - Log.e(TAG, "Activator is null."); - } - } - catch(Exception e){ - Log.e(TAG, e.getMessage(), e); - } - Log.d(TAG, "Have to register android bundle"); + startBundleFromStandaloneApp(bundleInfo); + }else if(bundleInfo.getID().equals(bundleId) && + bundleInfo.getPath().endsWith(".jar")){ // load classes from library + startBundleFromJar(bundleInfo); } }else{ nativeStartBundle(bundleId); } } - } /** @@ -324,6 +336,14 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI { return nativeListBundleResources(bundleId); } + /** + * Registers a bundle resource + * + * @param bundleId + * Id of the Bundle + * @param resource + * resource to be registered + */ public void registerResource(String bundleId, BundleResource resource){ Log.d(TAG, "register Resource"); // bundleResources.add(resource); @@ -331,7 +351,15 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI { resource.getURI(), resource.getResourceType(), resource.getName()); } - + + /** + * Returns the bundle configuration for the resources + * + * @param bundleId + * Id of the Bundle + * + * @return List All the resource configurations for the given bundle + */ public List getConfiguredBundleResources(String bundleId) { Log.d(TAG, "getConfiguredBundleResource " + bundleId); int configuredResources = getNumberOfConfiguredResources(bundleId); @@ -343,22 +371,42 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI { String[] resourceParams = getConfiguredResourceParams(bundleId, i); ResourceConfig config = new ResourceConfig(resourceParams); configs.add(config); - } return configs; } - + /** + * Unregisters a bundle resource + * + * @param resource + * Resource to be unregistered + */ public void unregisterResource(BundleResource resource){ Log.d(TAG, "unregister Resource"); nativeUnregisterBundleResource(resource, resource.getURI()); } + /** + * Returns the number of configured resources + * + * @param bundleId + * Id of the Bundle + * @return number of configured resources + */ public int getNumberOfConfiguredResources(String bundleId){ Log.d(TAG, "getNumberOfConfiguredResources"); return nativeGetNumberOfConfiguredResources(bundleId); } + /** + * Provides the configured resource parameter + * + * @param bundleId + * Id of the Bundle + * @param resId + Continuous numeric identifier within the bundle + * @return resource paramaters such as URI, resource type, name, etc. for the resource + */ public String[] getConfiguredResourceParams(String bundleId, int resId){ Log.d(TAG, "getConfiguredResourceParams"); return nativeGetConfiguredResourceParams(bundleId, resId); diff --git a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainerBundleAPI.java b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainerBundleAPI.java index 92954aa..721a7fb 100644 --- a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainerBundleAPI.java +++ b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainerBundleAPI.java @@ -31,7 +31,8 @@ import java.util.Vector; /** * This class provides APIs for managing the container and bundles in the - * container. + * container. The container provides such an interface to the bundle developer + * to access the configuration and to register/unregister resources. */ public interface RcsResourceContainerBundleAPI{ diff --git a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/ResourceConfig.java b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/ResourceConfig.java index dd308f1..e0d0879 100644 --- a/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/ResourceConfig.java +++ b/service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/ResourceConfig.java @@ -31,7 +31,6 @@ public class ResourceConfig { * Empty constructor for resoure config. */ public ResourceConfig() { - } /** diff --git a/service/resource-container/android/resource-container/src/main/jni/JniRcsResourceContainer.cpp b/service/resource-container/android/resource-container/src/main/jni/JniRcsResourceContainer.cpp index 09ef002..3bd2beb 100644 --- a/service/resource-container/android/resource-container/src/main/jni/JniRcsResourceContainer.cpp +++ b/service/resource-container/android/resource-container/src/main/jni/JniRcsResourceContainer.cpp @@ -85,7 +85,8 @@ namespace JavaLocalString version{env, newStringObject(env, bundleInfo->getVersion()) }; return env->NewObject(g_cls_RCSBundleInfo, g_ctor_RCSBundleInfo, - id.get(), path.get(), activatorName.get(), libraryPath.get(), version.get()); + id.get(), path.get(), activatorName.get(), libraryPath.get(), version.get(), + bundleInfo->isActivated()); } } diff --git a/service/resource-container/examples/android/AndroidBundle/app/build.gradle b/service/resource-container/examples/android/AndroidBundle/app/build.gradle index 043b375..9e88223 100755 --- a/service/resource-container/examples/android/AndroidBundle/app/build.gradle +++ b/service/resource-container/examples/android/AndroidBundle/app/build.gradle @@ -1,11 +1,10 @@ -apply plugin: 'com.android.application' +apply plugin: 'com.android.library' android { compileSdkVersion 23 buildToolsVersion "22.0.1" defaultConfig { - applicationId "org.iotivity.service.sample.androidbundle" minSdkVersion 21 targetSdkVersion 23 versionCode 1 diff --git a/service/resource-container/examples/android/RCSampleServerApp/app/src/main/assets/lib/ResourceContainerConfig.xml b/service/resource-container/examples/android/RCSampleServerApp/app/src/main/assets/lib/ResourceContainerConfig.xml index da6e17f..493d638 100755 --- a/service/resource-container/examples/android/RCSampleServerApp/app/src/main/assets/lib/ResourceContainerConfig.xml +++ b/service/resource-container/examples/android/RCSampleServerApp/app/src/main/assets/lib/ResourceContainerConfig.xml @@ -37,7 +37,7 @@ - + \ No newline at end of file diff --git a/service/resource-container/include/RCSBundleInfo.h b/service/resource-container/include/RCSBundleInfo.h index 31d2902..323cf54 100644 --- a/service/resource-container/include/RCSBundleInfo.h +++ b/service/resource-container/include/RCSBundleInfo.h @@ -82,6 +82,14 @@ namespace OIC */ virtual const std::string &getVersion() = 0; + /** + * API for getting the activation status of the bundle + * + * @return activation status of the bundle + * + */ + virtual bool isActivated() = 0; + RCSBundleInfo(); virtual ~RCSBundleInfo(); protected: diff --git a/service/resource-container/src/ResourceContainerImpl.cpp b/service/resource-container/src/ResourceContainerImpl.cpp index ce57b2b..62c7dda 100644 --- a/service/resource-container/src/ResourceContainerImpl.cpp +++ b/service/resource-container/src/ResourceContainerImpl.cpp @@ -212,6 +212,11 @@ namespace OIC ((BundleInfoInternal *) bundleInfo)->setJavaBundle(true); ((BundleInfoInternal *) bundleInfo)->setSoBundle(false); registerJavaBundle(bundleInfo); +#else + // android .jar library + ((BundleInfoInternal *) bundleInfo)->setSoBundle(false); + ((BundleInfoInternal *) bundleInfo)->setJavaBundle(false); + registerExtBundle(bundleInfo); #endif } else if(has_suffix(bundleInfo->getPath(), ".so")) -- 2.7.4