package com.kedzie.vbox.soap; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; import android.util.Base64; import android.util.Log; import com.google.common.base.Objects; import com.kedzie.vbox.BuildConfig; import com.kedzie.vbox.api.IDHCPServer; import com.kedzie.vbox.api.IDisplay; import com.kedzie.vbox.api.IEvent; import com.kedzie.vbox.api.IHost; import com.kedzie.vbox.api.IHostNetworkInterface; import com.kedzie.vbox.api.IMachine; import com.kedzie.vbox.api.IMachineStateChangedEvent; import com.kedzie.vbox.api.IManagedObjectRef; import com.kedzie.vbox.api.IMedium; import com.kedzie.vbox.api.INetworkAdapter; import com.kedzie.vbox.api.IProgress; import com.kedzie.vbox.api.ISession; import com.kedzie.vbox.api.ISessionStateChangedEvent; import com.kedzie.vbox.api.ISnapshotDeletedEvent; import com.kedzie.vbox.api.ISnapshotTakenEvent; import com.kedzie.vbox.api.IVirtualBox; import com.kedzie.vbox.api.Screenshot; import com.kedzie.vbox.api.jaxb.LockType; import com.kedzie.vbox.api.jaxb.MachineState; import com.kedzie.vbox.api.jaxb.VBoxEventType; import com.kedzie.vbox.app.Tuple; import com.kedzie.vbox.app.Utils; import com.kedzie.vbox.metrics.MetricQuery; import com.kedzie.vbox.server.Server; import com.kedzie.vbox.soap.ssl.InteractiveTrustedHttpsTransport; import com.kedzie.vbox.soap.ssl.KeystoreTrustedHttpsTransport; import org.ksoap2.SoapEnvelope; import org.ksoap2.SoapFault; import org.ksoap2.serialization.KvmSerializable; import org.ksoap2.serialization.PropertyInfo; import org.ksoap2.serialization.SoapObject; import org.ksoap2.serialization.SoapPrimitive; import org.ksoap2.serialization.SoapSerializationEnvelope; import org.ksoap2.transport.HttpTransportSE; import org.xmlpull.v1.XmlPullParserException; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Array; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.net.ConnectException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import nf.fr.eraasoft.pool.ObjectPool; import nf.fr.eraasoft.pool.PoolException; import nf.fr.eraasoft.pool.PoolSettings; import nf.fr.eraasoft.pool.PoolableObjectBase; /** * VirtualBox JAX-WS API * @apiviz.landmark * @apiviz.stereotype service * @apiviz.owns com.kedzie.vbox.api.IVirtualBox * @apiviz.owns com.kedzie.vbox.soap.HttpTransport * @apiviz.owns com.kedzie.vbox.soap.TrustedHttpsTransport * @apiviz.owns com.kedzie.vbox.server.Server * @apiviz.uses com.kedzie.vbox.soap.KSOAP * @apiviz.composedOf com.kedzie.vbox.soap.VBoxSvc$KSOAPInvocationHandler */ public class VBoxSvc implements Parcelable, Externalizable { private static final String TAG = "VBoxSvc"; private static final int TIMEOUT = 20000; public static final String BUNDLE = "vmgr"; public static final String NAMESPACE = "http://www.virtualbox.org/"; private static final int THREAD_POOL_SIZE = 15; private static final int TRANSPORT_POOL_SIZE = 15; private static final ClassLoader LOADER = VBoxSvc.class.getClassLoader(); public static final Parcelable.Creator<VBoxSvc> CREATOR = new Parcelable.Creator<VBoxSvc>() { public VBoxSvc createFromParcel(Parcel in) { VBoxSvc svc = new VBoxSvc((Server)in.readParcelable(LOADER)); String vboxId = in.readString(); svc._vbox = svc.getProxy(IVirtualBox.class, vboxId); svc.init(); return svc; } public VBoxSvc[] newArray(int size) { return new VBoxSvc[size]; } }; /** * Thread for making SOAP invocations */ public class AsynchronousThread implements Runnable { private String name; private SerializationEnvelope envelope; public AsynchronousThread(String name, SerializationEnvelope envelope) { this.name=name; this.envelope = envelope; } @Override public void run() { HttpTransportSE transport = null; try { transport = _transportPool.getObj(); transport.call(name, envelope); } catch(Exception e) { Log.e(TAG, "PoolException", e); } finally { _transportPool.returnObj(transport); } } } // // /** // * Make remote calls to VBox JAXWS API based on method // * metadata from {@link KSOAP} annotations. JavAssist implementation. // */ // public class KSOAPMethodHandler implements MethodHandler { // // public KSOAPMethodHandler(String id, Class<?> type, Map<String,Object> cache) { // _uiud=id; // _type=type; // _cache = cache!=null ? cache : new HashMap<String, Object>(); // } // // @Override // public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { // String name = method.getName(); // // KSOAP ksoap = method.getAnnotation(KSOAP.class); // if(ksoap==null) // ksoap=method.getDeclaringClass().getAnnotation(KSOAP.class); // // String cacheKey = name; // if(ksoap.cacheable()) { // if(args!=null) { // for(Object arg : args) // cacheKey+="-"+arg.toString(); // } // if(_cache.containsKey(cacheKey)) // return _cache.get(cacheKey); // } // // SoapObject request = new SoapObject(NAMESPACE, (Utils.isEmpty(ksoap.prefix()) ? _type.getSimpleName() : ksoap.prefix())+"_"+method.getName()); // // if (!Utils.isEmpty(ksoap.thisReference())) // request.addProperty(ksoap.thisReference(), _uiud); // // if(args!=null) { // for(int i=0; i<args.length; i++) // marshal(request, Utils.getAnnotation(KSOAP.class, method.getParameterAnnotations()[i]), method.getParameterTypes()[i], method.getGenericParameterTypes()[i], args[i]); // } // // SerializationEnvelope envelope = new SerializationEnvelope(request); // // if(method.isAnnotationPresent(Asyncronous.class)) { // _threadPoolExecutor.execute(new AsynchronousThread(NAMESPACE+request.getName(), envelope)); // return null; // } else { // HttpTransportSE transport = null; // try { // transport = _transportPool.getObj(); // transport.call(NAMESPACE+request.getName(), envelope); // Object ret = envelope.getResponse(method.getReturnType(), method.getGenericReturnType()); // if(ksoap.cacheable()) // _cache.put(cacheKey, ret); // if(name.startsWith("set")) //update cache if we are calling a setter // _cache.put("get"+name.substring(3), ret); // return ret; // } catch(PoolException e) { // Log.e(TAG, "PoolException", e); // throw new RuntimeException(e.getMessage(), e); // } finally { // _transportPool.returnObj(transport); // } // } // } // // /** // // * Add an argument to a SOAP request // * @param request SOAP request // * @param ksoap parameter annotation with marshalling configuration // * @param clazz {@link Class} of parameter // * @param gType Generic type of parameter // * @param obj object to marshall // // */ // private void marshal(SoapObject request, KSOAP ksoap, Class<?> clazz, Type gType, Object obj) { // if(obj==null) return; // if(clazz.isArray()) { //Arrays // for(Object o : (Object[])obj) // marshal( request, ksoap, clazz.getComponentType(), gType, o ); // } else if(Collection.class.isAssignableFrom(clazz)) { //Collections // Class<?> pClazz = Utils.getTypeParameter(gType,0); // for(Object o : (List<?>)obj) // marshal(request, ksoap, pClazz, gType, o ); // } else if(!Utils.isEmpty(ksoap.type())) //if annotation specifies SOAP datatype, i.e. unsignedint // request.addProperty( ksoap.value(), new SoapPrimitive(ksoap.namespace(), ksoap.type(), obj.toString())); // else if(IManagedObjectRef.class.isAssignableFrom(clazz)) // request.addProperty(ksoap.value(), ((IManagedObjectRef)obj).getIdRef() ); // else if(clazz.isEnum()) // request.addProperty(ksoap.value(), new SoapPrimitive(NAMESPACE, clazz.getSimpleName(), obj.toString() )); // else // request.addProperty(ksoap.value(), obj); // } // } // // /** // * Base class for SOAP proxies // */ // @KSOAP(prefix="IManagedObjectRef") // public class ManagedObjectRef implements IManagedObjectRef, Parcelable { // // /** Unique identifier (UIUD) of {@link ManagedObjectRef} */ // private String _uiud; // // /** Type of {@link IManagedObjectRef} */ // private Class<?> _type; // // /** Cached property values */ // private Map<String, Object> _cache; // // /** // * Get the managed object identifier // * @return Unique identifier (UIUD) of {@link ManagedObjectRef} // */ // @Override // public String getIdRef() { // return _uiud; // } // // /** // * Get property cache // */ // @Override // public Map<String, Object> getCache() { // return _cache; // } // // /** // * Clear all cached property values // */ // @Override // public void clearCache() { // _cache.clear(); // } // // /** // * Clear specific cached property values // * @param names names of properties to clear // */ // @Override // public void clearCacheNamed(String...names) { // for(String arg : (String[])args[0]) // _cache.remove(arg); // } // // /** // * @return VirtualBox JAXWS API // */ // @Override // public VBoxSvc getAPI() { // return VBoxSvc.this; // } // // /** // * Returns the name of the interface that this managed object represents, for example "IMachine", as a string. // */ // @Override // @KSOAP(prefix="IManagedObjectRef") // public String getInterfaceName() { // return _type; // } // // @Override // public int hashCode() { // return Objects.hashCode(_uiud); // } // // @Override // public boolean equals(Object other) { // if(other==null || !this.getClass().equals(other.getClass())) // return false; // IManagedObjectRef that = (IManagedObjectRef)other; // return Objects.equal(_uiud, that.getIdRef()); // } // // @Override // public String toString() { // return _type.getSimpleName() + "\t#" + _uiud + "\t" + Utils.toString("Cache", _cache); // } // // @Override // public int describeContents() { // return 0; // } // // @Override // public void writeToParcel(Parcel dest, int flags) { // dest.writeParcelable(VBoxSvc.this, 0); // dest.writeString(_uiud); // dest.writeMap(_cache); // } // // /** // * Releases this managed object reference and frees the resources that were allocated for it in the webservice server process. // * After calling this method the identifier of the reference can no longer be used. // */ // @Override // @KSOAP(prefix="IManagedObjectRef") // public void release(); // } // public static <T> T getJavassistProxy(Class<T> superclazz) throws Exception { // ProxyFactory factory = new ProxyFactory(); // factory.setSuperclass(superclazz); // Class clazz = factory.createClass(); // Object instance = clazz.newInstance(); // ((ProxyObject) instance).setHandler(new KSOAPMethodHandler(instance)); // return (T) instance; // } /** * Make remote calls to VBox JAXWS API based on method metadata from {@link KSOAP} annotations. */ public class KSOAPInvocationHandler implements InvocationHandler { /** Unique identifier (UIUD) of {@link IManagedObjectRef} */ private String _uiud; /** Type of {@link IManagedObjectRef} */ private Class<?> _type; /** cached property values */ private Map<String, Object> _cache; public KSOAPInvocationHandler(String id, Class<?> type, Map<String,Object> cache) { _uiud=id; _type=type; _cache = cache!=null ? cache : new HashMap<String, Object>(); } @Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { String name = method.getName(); if(name.equals("hashCode")) return Objects.hashCode(_uiud); if(name.equals("toString")) { return _type.getSimpleName() + " #" + _uiud + Utils.toString("Cache", _cache); } if(name.equals("equals")) { if(args[0]==null) return false; if(!(args[0] instanceof IManagedObjectRef) || !_type.isAssignableFrom(args[0].getClass())) return false; return Objects.equal(_uiud, ((IManagedObjectRef)args[0]).getIdRef()); } if(name.equals("clearCache")) { _cache.clear(); return null; } if(name.equals("clearCacheNamed")) { for(String arg : (String[])args[0]) _cache.remove(arg); return null; } if(name.equals("getAPI")) return VBoxSvc.this; if(name.equals("describeContents")) return 0; if(name.equals("getCache")) return _cache; if(name.equals("getIdRef")) return _uiud; if(name.equals("writeToParcel")) { Parcel out = (Parcel)args[0]; out.writeParcelable(VBoxSvc.this, 0); out.writeString(_uiud); out.writeMap(_cache); return null; } KSOAP ksoap = method.getAnnotation(KSOAP.class); if(ksoap==null) ksoap=method.getDeclaringClass().getAnnotation(KSOAP.class); String cacheKey = name; if(ksoap.cacheable()) { if(args!=null) { for(Object arg : args) cacheKey+="-"+arg.toString(); } if(_cache.containsKey(cacheKey)) return _cache.get(cacheKey); } SoapObject request = new SoapObject(NAMESPACE, (Utils.isEmpty(ksoap.prefix()) ? _type.getSimpleName() : ksoap.prefix())+"_"+method.getName()); if (!Utils.isEmpty(ksoap.thisReference())) request.addProperty(ksoap.thisReference(), _uiud); if(args!=null) { for(int i=0; i<args.length; i++) marshal(request, Utils.getAnnotation(KSOAP.class, method.getParameterAnnotations()[i]), method.getParameterTypes()[i], method.getGenericParameterTypes()[i], args[i]); } SerializationEnvelope envelope = new SerializationEnvelope(request); if(method.isAnnotationPresent(Asyncronous.class)) { _threadPoolExecutor.execute(new AsynchronousThread(NAMESPACE+request.getName(), envelope)); return null; } else { HttpTransportSE transport = null; try { transport = _transportPool.getObj(); transport.call(NAMESPACE+request.getName(), envelope); Object ret = envelope.getResponse(method.getReturnType(), method.getGenericReturnType()); if(ksoap.cacheable()) _cache.put(cacheKey, ret); if(name.startsWith("set")) //update cache if we are calling a setter _cache.put("get"+name.substring(3), ret); return ret; } catch(PoolException e) { Log.e(TAG, "PoolException", e); throw new RuntimeException(e.getMessage(), e); } finally { _transportPool.returnObj(transport); } } } /** * Add an argument to a SOAP request * @param request SOAP request * @param ksoap parameter annotation with marshalling configuration * @param clazz {@link Class} of parameter * @param gType Generic type of parameter * @param obj object to marshall */ private void marshal(SoapObject request, KSOAP ksoap, Class<?> clazz, Type gType, Object obj) { if(obj==null) return; if(clazz.isArray()) { //Arrays for(Object o : (Object[])obj) marshal( request, ksoap, clazz.getComponentType(), gType, o ); } else if(Collection.class.isAssignableFrom(clazz)) { //Collections Class<?> pClazz = Utils.getTypeParameter(gType,0); for(Object o : (List<?>)obj) marshal(request, ksoap, pClazz, gType, o ); } else if(!Utils.isEmpty(ksoap.type())) //if annotation specifies SOAP datatype, i.e. unsignedint request.addProperty( ksoap.value(), new SoapPrimitive(ksoap.namespace(), ksoap.type(), obj.toString())); else if(IManagedObjectRef.class.isAssignableFrom(clazz)) request.addProperty(ksoap.value(), ((IManagedObjectRef)obj).getIdRef() ); else if(clazz.isEnum()) request.addProperty(ksoap.value(), new SoapPrimitive(NAMESPACE, clazz.getSimpleName(), obj.toString() )); else request.addProperty(ksoap.value(), obj); } } /** * Handles unmarshalling of SOAP response based on {@link KSOAP} annotation metadata */ public class SerializationEnvelope extends SoapSerializationEnvelope { /** Reflection cache. Maps from [classname].[property] to [setter-method] */ private Map<String, Method> typeCache = new HashMap<String, Method>(); public SerializationEnvelope(SoapObject soapObject) { super(SoapEnvelope.VER11); setAddAdornments(false); setOutputSoapObject(soapObject); } /** * Unmarshall SoapEnvelope to correct type * @param returnType type to umarshall * @param genericType parameterized type * @return unmarshalled return value * @throws SoapFault */ public Object getResponse(Class<?> returnType, Type genericType) throws SoapFault { if (bodyIn instanceof SoapFault) throw (SoapFault) bodyIn; boolean IS_COLLECTION = Collection.class.isAssignableFrom(returnType); boolean IS_MAP = Map.class.isAssignableFrom(returnType); boolean IS_ARRAY = returnType.isArray() && !returnType.getComponentType().equals(byte.class); KvmSerializable ks = (KvmSerializable) bodyIn; if ((ks.getPropertyCount()==0 && !IS_COLLECTION && !IS_MAP)) { Log.w(TAG, "returning NULL because property count is 0"); return null; } if(IS_MAP) { Type valueType = ((ParameterizedType)genericType).getActualTypeArguments()[1]; if(!(valueType instanceof Class)) { //Map<String, List<String>> Map<String, List<String>> map = new HashMap<String, List<String>>(); PropertyInfo info = new PropertyInfo(); for (int i = 0; i < ks.getPropertyCount(); i++) { ks.getPropertyInfo(i, null, info); if (!map.containsKey(info.getName())) map.put(info.getName(), new ArrayList<String>()); map.get(info.getName()).add(ks.getProperty(i).toString()); } return map; } else { //Map<String,String> Map<String, String> map = new HashMap<String, String>(); PropertyInfo info = new PropertyInfo(); for (int i = 0; i < ks.getPropertyCount(); i++) { ks.getPropertyInfo(i, null, info); map.put(info.getName(), ks.getProperty(i).toString()); } return map; } } else if(IS_COLLECTION) { Class<?> pClazz = Utils.getTypeParameter(genericType,0); Collection<Object> list = new ArrayList<Object>(ks.getPropertyCount()); for (int i = 0; i < ks.getPropertyCount(); i++) list.add(unmarshal(pClazz, genericType, ks.getProperty(i))); return list; } else if(IS_ARRAY) { Class<?> pClazz = returnType.getComponentType(); Object[] array = (Object[])Array.newInstance(pClazz, ks.getPropertyCount()); for (int i = 0; i < ks.getPropertyCount(); i++) array[i] = unmarshal(pClazz, genericType, ks.getProperty(i)); return array; } return unmarshal(returnType, genericType, ks.getProperty(0)); } /** * convert string return value to correct type * @param returnType remote method return type * @param genericType remote method return type (parameterized) * @param ret marshalled value * @return unmarshalled return value */ public Object unmarshal(Class<?> returnType, Type genericType, Object ret) { if(ret==null || ret.toString().equals("anyType{}")) return null; if(returnType.isArray() && returnType.getComponentType().equals(byte.class)) { return android.util.Base64.decode(ret.toString().getBytes(), android.util.Base64.DEFAULT); } else if(returnType.equals(Boolean.class) || returnType.equals(boolean.class)) { return Boolean.valueOf(ret.toString()); } else if(returnType.equals(Integer.class) || returnType.equals(int.class)) { return Integer.valueOf(ret.toString()); } else if(returnType.equals(Long.class) || returnType.equals(long.class)) { return Long.valueOf(ret.toString()); } else if(returnType.equals(String.class)) return ret.toString(); else if(IManagedObjectRef.class.isAssignableFrom(returnType)) return getProxy(returnType, ret.toString()); else if(returnType.isEnum()) { for( Object element : returnType.getEnumConstants()) if( element.toString().equals( ret.toString() ) ) return element; } else if(returnType.isAnnotationPresent(KSoapObject.class)) { try { if(BuildConfig.DEBUG) Log.v(TAG, "Unmarshalling Complex Object: " + returnType.getName()); Object pojo = returnType.newInstance(); SoapObject soapObject = (SoapObject)ret; PropertyInfo propertyInfo = new PropertyInfo(); for(int i=0; i<soapObject.getPropertyCount(); i++) { soapObject.getPropertyInfo(i, propertyInfo); Method setterMethod = findSetterMethod(returnType, propertyInfo.getName()); if(setterMethod==null) continue; Class<?> propertyType = setterMethod.getParameterTypes()[0]; Object value = unmarshal(propertyType, propertyType, propertyInfo.getValue()); if(BuildConfig.DEBUG) Log.v(TAG, String.format("Setting property: %1$s.%2$s = %3$s", returnType.getSimpleName(), propertyInfo.getName(), value)); setterMethod.invoke(pojo, value); } return pojo; } catch (Exception e) { Log.e(TAG, "Error unmarshalling complex object: " + returnType.getName(), e); } } return ret; } /** * Find and cache the setter method for a particular property * @param clazz object type * @param property property name * @return the setter method */ private Method findSetterMethod(Class<?> clazz, String property) { String key = clazz.getSimpleName()+"."+property; if(typeCache.containsKey(key)) return typeCache.get(key); String setterMethodName = "set"+property.substring(0, 1).toUpperCase(Locale.ENGLISH)+property.substring(1); for(Method method : clazz.getMethods()) if(method.getName().equals(setterMethodName)) { typeCache.put(key, method); return method; } Log.w(TAG, "No Setter Found: " + setterMethodName); return null; } } private Server _server; private IVirtualBox _vbox; private ObjectPool<HttpTransportSE> _transportPool; private ExecutorService _threadPoolExecutor; /** * @param server VirtualBox webservice server */ public VBoxSvc(Server server) { _server=server; init(); } /** * Copy constructor * @param copy The original {@link VBoxSvc} to copy */ public VBoxSvc(VBoxSvc copy) { this(copy._server); _vbox = getProxy(IVirtualBox.class, copy._vbox.getIdRef()); } private void init() { _threadPoolExecutor = Executors.newFixedThreadPool(THREAD_POOL_SIZE); PoolSettings<HttpTransportSE> poolSettings = new PoolSettings<HttpTransportSE>( new PoolableObjectBase<HttpTransportSE>() { @Override public void activate(HttpTransportSE t) {} @Override public HttpTransportSE make() { return _server.isSSL() ? new KeystoreTrustedHttpsTransport(_server, TIMEOUT) : new HttpTransport(_server, TIMEOUT); } }); poolSettings.min(0).max(TRANSPORT_POOL_SIZE); _transportPool = poolSettings.pool(); } public IVirtualBox getVBox() { return _vbox; } public void setVBox(IVirtualBox box) { _vbox=box; } public Server getServer() { return _server; } public ExecutorService getExecutor() { return _threadPoolExecutor; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(_server, flags); dest.writeString(_vbox.getIdRef()); } @Override public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException { Log.w(TAG, "===========VBoxSvc has been SERIALIZED==========="); _server=(Server)input.readObject(); init(); String vboxId = input.readUTF(); _vbox = getProxy(IVirtualBox.class, vboxId); } @Override public void writeExternal(ObjectOutput output) throws IOException { Log.w(TAG, "===========VBoxSvc has been SERIALIZED==========="); output.writeObject(_server); output.writeUTF(_vbox.getIdRef()); } /** * Create remote-invocation proxy w/o cached properties * @param clazz type of {@link IManagedObjectRef} * @param id UIUD of {@link IManagedObjectRef} * @return remote invocation proxy */ public <T> T getProxy(Class<T> clazz, String id) { return getProxy(clazz, id, null); } /** * Create remote-invocation proxy w/cached properties * @param clazz type of {@link IManagedObjectRef} * @param id UIUD of {@link IManagedObjectRef} * @param cache cached properties * @return remote invocation proxy */ public <T> T getProxy(Class<T> clazz, String id, Map<String, Object> cache) { T proxy = clazz.cast( Proxy.newProxyInstance(LOADER, new Class [] { clazz }, new KSOAPInvocationHandler(id, clazz, cache))); if(IEvent.class.equals(clazz)) { VBoxEventType type = ((IEvent)proxy).getType(); if(type.equals(VBoxEventType.ON_MACHINE_STATE_CHANGED)) return clazz.cast(getProxy( IMachineStateChangedEvent.class, id, cache )); else if(type.equals(VBoxEventType.ON_SESSION_STATE_CHANGED)) return clazz.cast(getProxy( ISessionStateChangedEvent.class, id, cache )); else if(type.equals(VBoxEventType.ON_SNAPSHOT_DELETED)) return clazz.cast(getProxy( ISnapshotDeletedEvent.class, id, cache )); else if(type.equals(VBoxEventType.ON_SNAPSHOT_TAKEN)) return clazz.cast(getProxy( ISnapshotTakenEvent.class, id, cache )); } return proxy; } /** * Connect to <code>vboxwebsrv</code> & initialize the VBoxSvc API interface * @return initialized {@link IVirtualBox} API interface * @throws IOException * @throws XmlPullParserException */ public IVirtualBox logon() throws IOException { try { return (_vbox = getProxy(IVirtualBox.class, null).logon(_server.getUsername(), _server.getPassword())); } catch(SoapFault e) { Log.e(TAG, "Logon error", e); throw new ConnectException("Authentication Error"); } } /** * Logoff from VirtualBox API * @throws IOException */ public void logoff() throws IOException { if(_vbox!=null) _vbox.logoff(); _vbox=null; } /** * Query metric data for specified {@link IManagedObjectRef} * @param object object to get metrics for * @param metrics specify which metrics/accumulations to query. * for all * @return {@link Map} from metric name to {@link MetricQuery} * @throws IOException */ public Map<String, MetricQuery> queryMetrics(String object, String...metrics) throws IOException { Map<String, List<String>> data= _vbox.getPerformanceCollector().queryMetricsData(metrics, new String[] { object }); Map<String, MetricQuery> ret = new HashMap<String, MetricQuery>(); for(int i=0; i<data.get("returnMetricNames").size(); i++) { MetricQuery q = new MetricQuery(); q.name=(String)data.get("returnMetricNames").get(i); q.object=(String)data.get("returnObjects").get(i); q.scale=Integer.valueOf(data.get("returnScales").get(i)); q.unit=(String)data.get("returnUnits").get(i); int start = Integer.valueOf( data.get("returnDataIndices").get(i)); int length = Integer.valueOf( data.get("returnDataLengths").get(i)); q.values= new int[length]; int j=0; for(String s : data.get("returnval").subList(start, start+length)) q.values[j++] = Integer.valueOf(s)/q.scale; ret.put(q.name, q); } return ret; } public Screenshot takeScreenshot(IMachine machine) throws IOException { if(machine.getState().equals(MachineState.RUNNING) || machine.getState().equals(MachineState.SAVED)) { ISession session = _vbox.getSessionObject(); machine.lockMachine(session, LockType.SHARED); try { IDisplay display = session.getConsole().getDisplay(); Map<String, String> res = display.getScreenResolution(0); int width = Integer.valueOf(res.get("width")); int height = Integer.valueOf(res.get("height")); return new Screenshot(width, height, display.takeScreenShotPNGToArray(0, width, height)); } finally { session.unlockMachine(); } } return null; } public Screenshot takeScreenshot(IMachine machine, int width, int height) throws IOException { ISession session = _vbox.getSessionObject(); machine.lockMachine(session, LockType.SHARED); try { IDisplay display = session.getConsole().getDisplay(); Map<String, String> res = display.getScreenResolution(0); float screenW = Float.valueOf(res.get("width")); float screenH = Float.valueOf(res.get("height")); if(screenW > screenH) { float aspect = screenH/screenW; height =(int) (aspect*width); } else if(screenH > screenW){ float aspect = screenW/screenH; width =(int) (aspect*height); } return new Screenshot(width, height, session.getConsole().getDisplay().takeScreenShotPNGToArray(0, width, height)); } finally { session.unlockMachine(); } } public Screenshot readSavedScreenshot(IMachine machine, int screenId) throws IOException { Map<String, String> val = machine.readSavedScreenshotPNGToArray(screenId); return new Screenshot(Integer.valueOf(val.get("width")), Integer.valueOf(val.get("height")), Base64.decode(val.get("returnval"), 0)); } public Screenshot readSavedThumbnail(IMachine machine, int screenId) throws IOException { Map<String, String> val = machine.readSavedThumbnailPNGToArray(screenId); return new Screenshot(Integer.valueOf(val.get("width")), Integer.valueOf(val.get("height")), Base64.decode(val.get("returnval"), 0)); } /** * Load network properties * @param adapter the network adapter * @param names Property names to load, or empty for all * @return * @throws IOException */ public Properties getProperties(INetworkAdapter adapter, String...names) throws IOException { StringBuffer nameString = new StringBuffer(); for(String name : names) Utils.appendWithComma(nameString, name); return getProperties(adapter.getProperties(nameString.toString())); } /** * Load medium properties * @param medium the medium * @param names Property names to load, or empty for all * @return properties * @throws IOException */ public Properties getProperties(IMedium medium, String...names) throws IOException { StringBuffer nameString = new StringBuffer(); for(String name : names) Utils.appendWithComma(nameString, name); return getProperties(medium.getProperties(nameString.toString()) ); } private Properties getProperties(Map<String, List<String>> val, String...names) throws IOException { List<String> returnNames = val.get("returnNames"); List<String> values = val.get("returnval"); Properties properties = new Properties(); for(int i=0; i<returnNames.size(); i++) properties.put(returnNames.get(i), values.get(i)); return properties; } public Tuple<IHostNetworkInterface, IProgress> createHostOnlyNetworkInterface(IHost host) throws IOException { Map<String, String> val = host.createHostOnlyNetworkInterface(); IHostNetworkInterface networkInterface = getProxy(IHostNetworkInterface.class, val.get("hostInterface")); IProgress progress = getProxy(IProgress.class, val.get("returnval")); return new Tuple<IHostNetworkInterface, IProgress>(networkInterface, progress); } /** * Searches a DHCP server settings to be used for the given internal network name. * <p><dl><dt><b>Expected result codes:</b></dt><dd><table><tbody><tr> * <td>{@link IVirtualBox#E_INVALIDARG}</td><td>Host network interface <em>name</em> already exists. </td></tr> * </tbody></table></dd></dl></p> * @param name server name * @param server DHCP server settings */ public IDHCPServer findDHCPServerByNetworkName(String name) throws IOException { try { return getVBox().findDHCPServerByNetworkName(name); } catch(SoapFault e) { Log.e(TAG, "Couldn't find DHCP Server: " + e.getMessage()); return null; } } /** * Ping a HTTPS server using SSL and prompt user to trust certificate * @param handler {@link Handler} to prompt user to trust server certificate * @throws IOException * @throws XmlPullParserException */ public void ping(Handler handler) throws IOException, XmlPullParserException { SerializationEnvelope envelope = new SerializationEnvelope( new SoapObject(NAMESPACE, "IManagedObjectRef_getInterfaceName").addProperty("_this", "0")); new InteractiveTrustedHttpsTransport(_server, TIMEOUT, handler).call(NAMESPACE+"IManagedObjectRef_getInterfaceName", envelope); } }