/*******************************************************************************
 * Copyright (c) 1997, 2018 by ProSyst Software GmbH
 * http://www.prosyst.com
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    ProSyst Software GmbH - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.internal.util.security;

import java.io.*;
import java.util.Dictionary;
import org.eclipse.equinox.internal.util.UtilActivator;
import org.osgi.framework.*;

/**
 * Utility class to execute common privileged code.
 * 
 * @author Valentin Valchev
 * @author Pavlin Dobrev
 * @version 1.0
 */

public final class SecurityUtil implements PrivilegedRunner.PrivilegedDispatcher {

	private Object controlContext;

	private static final int SYSTEM_GET_PROPERTY = 41;
	private static final int CREATE_THREAD = 42;
	private static final int CLASS_FOR_NAME = 43;
	private static final int SYSTEM_SET_PROPERTY = 44;

	private static final int FILE_BASE = 50;
	private static final int FILE_GET_INPUT_STREAM = FILE_BASE + 0;
	private static final int FILE_GET_OUTPUT_STREAM = FILE_BASE + 1;
	private static final int FILE_LENGTH = FILE_BASE + 2;
	private static final int FILE_EXISTS = FILE_BASE + 3;
	private static final int FILE_ISDIR = FILE_BASE + 4;
	private static final int FILE_LAST_MODIFIED = FILE_BASE + 5;
	private static final int FILE_LIST = FILE_BASE + 6;
	private static final int FILE_DELETE = FILE_BASE + 7;
	private static final int FILE_RENAME = FILE_BASE + 8;
	private static final int FILE_GET_RANDOM_ACCESS_FILE = FILE_BASE + 9;

	private static final int SERVICE_BASE = 60;
	private static final int SERVICE_GET_REFERENCE = SERVICE_BASE + 0;
	private static final int SERVICE_GET_SERVICE = SERVICE_BASE + 1;
	private static final int SERVICE_REG_CLASS = SERVICE_BASE + 2;
	private static final int SERVICE_REG_CLASSES = SERVICE_BASE + 3;

	private static final int BUNDLE_BASE = 70;
	private static final int BUNDLE_GET_LOCATION = BUNDLE_BASE + 0;
	private static final int BUNDLE_GET_HEADERS = BUNDLE_BASE + 1;
	private static final int BUNDLE_START = BUNDLE_BASE + 2;
	private static final int BUNDLE_STOP = BUNDLE_BASE + 3;
	private static final int BUNDLE_UNINSTALL = BUNDLE_BASE + 4;
	private static final int BUNDLE_UPDATE = BUNDLE_BASE + 5;
	private static final int BUNDLE_UPDATE_IS = BUNDLE_BASE + 6;

	/**
	 * Constructs a new SecureAction object. The constructed SecureAction object
	 * uses the caller's AccessControlContext to perform security checks
	 */
	public SecurityUtil() {
		// save the control context to be used.
		SecurityManager sm = System.getSecurityManager();
		if (sm != null) {
			controlContext = sm.getSecurityContext();
		}
	}

	/**
	 * Creates a new Thread from a Runnable. Same as calling new
	 * Thread(target,name).
	 * 
	 * @param target
	 *            the Runnable to create the Thread from.
	 * @param name
	 *            The name of the Thread.
	 * @return The new Thread
	 */
	public Thread createThread(final Runnable target, final String name) {
		try {
			return (Thread) PrivilegedRunner.doPrivileged(controlContext, this, CREATE_THREAD, target, name, null, null);
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Returns a Class. Same as calling Class.forName(name)
	 * 
	 * @param name
	 *            the name of the class.
	 * @return a Class
	 * @throws ClassNotFoundException
	 */
	public Class<?> forName(final String name) throws ClassNotFoundException {
		try {
			return (Class<?>) PrivilegedRunner.doPrivileged(controlContext, this, CLASS_FOR_NAME, name, null, null, null);
		} catch (ClassNotFoundException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Returns a system property. Same as calling System.getProperty(String).
	 * 
	 * @param property
	 *            the property key.
	 * @return the value of the property or null if it does not exist.
	 */
	public String getProperty(final String property) {
		if (property == null) {
			throw new NullPointerException("property is null");
		}
		String ret;
		try {
			ret = (String) PrivilegedRunner.doPrivileged(controlContext, this, SYSTEM_GET_PROPERTY, property, null, null, null);
		} catch (Exception t) {
			ret = null;
		}
		return ret;
	}

	/**
	 * Returns a system property. Same as calling
	 * System.getProperty(String,String).
	 * 
	 * @param property
	 *            the property key.
	 * @param def
	 *            the default value if the property key does not exist.
	 * @return the value of the property or the default value if the property
	 *         does not exist.
	 */
	public String getProperty(final String property, final String def) {
		String ret = getProperty(property);
		return ret != null ? ret : def;
	}

	/**
	 * Returns a boolean system property. Same as calling
	 * Boolean.getBoolean(String).
	 * 
	 * @param property
	 *            the property key.
	 * @return the value of the property or <code>false</code>, if not set
	 */
	public boolean getBooleanProperty(final String property) {
		String ret = getProperty(property);
		return ret != null ? Boolean.valueOf(ret).booleanValue() : false;
	}

	/**
	 * Sets a system property. Same as System.setProperty()
	 * 
	 * @param key
	 *            the name of the property
	 * @param value
	 *            the value of the system property
	 * @return the old value of the property, or null
	 */
	public String setProperty(final String key, final String value) {
		if (key == null) {
			throw new NullPointerException("key is null");
		}
		if (value == null) {
			throw new NullPointerException("key is null");
		}
		try {
			return (String) PrivilegedRunner.doPrivileged(controlContext, this, SYSTEM_SET_PROPERTY, key, value, null, null);
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Creates a FileInputStream from a File. Same as calling new
	 * FileInputStream(File).
	 * 
	 * @param file
	 *            the File to create a FileInputStream from.
	 * @return The FileInputStream.
	 * @throws FileNotFoundException
	 *             if the File does not exist.
	 */
	public FileInputStream getFileInputStream(final File file) throws FileNotFoundException {
		try {
			return (FileInputStream) PrivilegedRunner.doPrivileged(controlContext, this, FILE_GET_INPUT_STREAM, file, null, null, null);
		} catch (FileNotFoundException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Creates a random access file
	 * 
	 * @param file
	 *            the file object
	 * @param mode
	 *            the open mode
	 * @return the random seekable file object
	 * @throws FileNotFoundException
	 *             if the File does not exist
	 */
	public RandomAccessFile getRandomAccessFile(final File file, final String mode) throws FileNotFoundException {
		try {
			return (RandomAccessFile) PrivilegedRunner.doPrivileged(controlContext, this, FILE_GET_RANDOM_ACCESS_FILE, file, mode, null, null);
		} catch (FileNotFoundException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Creates a FileInputStream from a File. Same as calling new
	 * FileOutputStream(File,boolean).
	 * 
	 * @param file
	 *            the File to create a FileOutputStream from.
	 * @param append
	 *            indicates if the OutputStream should append content.
	 * @return The FileOutputStream.
	 * @throws FileNotFoundException
	 *             if the File does not exist.
	 */
	public FileOutputStream getFileOutputStream(final File file, final boolean append) throws FileNotFoundException {
		try {
			return (FileOutputStream) PrivilegedRunner.doPrivileged(//
					controlContext, this, FILE_GET_OUTPUT_STREAM, file, //
					append ? Boolean.TRUE : Boolean.FALSE, null, null);
		} catch (FileNotFoundException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Returns true if a file exists, otherwise false is returned. Same as
	 * calling file.exists().
	 * 
	 * @param file
	 *            a file object
	 * @return true if a file exists, otherwise false
	 */
	public boolean exists(final File file) {
		try {
			return ((Boolean) PrivilegedRunner.doPrivileged(controlContext, this, FILE_EXISTS, file, null, null, null)).booleanValue();
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Returns true if a file is a directory, otherwise false is returned. Same
	 * as calling file.isDirectory().
	 * 
	 * @param file
	 *            a file object
	 * @return true if a file is a directory, otherwise false
	 */
	public boolean isDirectory(final File file) {
		try {
			return ((Boolean) PrivilegedRunner.doPrivileged(controlContext, this, FILE_ISDIR, file, null, null, null)).booleanValue();
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Returns the length of a file. Same as calling file.length().
	 * 
	 * @param file
	 *            a file object
	 * @return the length of a file or -1 if file doesn't exists
	 */
	public long length(final File file) {
		try {
			return ((Long) PrivilegedRunner.doPrivileged(controlContext, this, FILE_LENGTH, file, null, null, null)).longValue();
		} catch (Throwable t) {
			return -1L;
		}
	}

	/**
	 * Returns a file's last modified stamp. Same as calling
	 * file.lastModified().
	 * 
	 * @param file
	 *            a file object
	 * @return a file's last modified stamp or -1 if file doesn't exists
	 */
	public long lastModified(final File file) {
		try {
			return ((Long) PrivilegedRunner.doPrivileged(controlContext, this, FILE_LAST_MODIFIED, file, null, null, null)).longValue();
		} catch (Exception t) {
			return -1L;
		}
	}

	/**
	 * Returns a file's list. Same as calling file.list().
	 * 
	 * @param file
	 *            a file object
	 * @return a file's list.
	 */
	public String[] list(final File file) {
		try {
			return (String[]) PrivilegedRunner.doPrivileged(controlContext, this, FILE_LIST, file, null, null, null);
		} catch (Exception t) {
			return null;
		}
	}

	/**
	 * Deletes the specified file. Same as File.delete()
	 * 
	 * @param file
	 *            the file object
	 * @return if delete succeeded
	 */
	public boolean delete(final File file) {
		try {
			return ((Boolean) PrivilegedRunner.doPrivileged(controlContext, this, FILE_DELETE, file, null, null, null)).booleanValue();
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Renames the source file to the target name. Same as File.renameTo(File)
	 * 
	 * @param source
	 *            the file object, that will be renamed
	 * @param target
	 *            the target file name
	 * @return if rename succeeded
	 */
	public boolean renameTo(final File source, final File target) {
		try {
			return ((Boolean) PrivilegedRunner.doPrivileged(controlContext, this, FILE_RENAME, source, target, null, null)).booleanValue();
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Gets a service object. Same as calling context.getService(reference)
	 * 
	 * @param reference
	 *            the ServiceReference
	 * @param context
	 *            the BundleContext
	 * @return a service object
	 */
	public Object getService(final ServiceReference<?> reference, final BundleContext context) {
		if (context == null) {
			throw new NullPointerException("Context is null");
		}
		if (reference == null) {
			throw new NullPointerException("Reference is null");
		}
		try {
			return PrivilegedRunner.doPrivileged(controlContext, this, SERVICE_GET_SERVICE, context, reference, null, null);
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Gets a reference for the specified service(s). Same as calling
	 * context.getServiceReferences(class, filter)
	 * 
	 * @param clazz
	 *            the name of the requested service class
	 * @param filter
	 *            an LDAP filter
	 * @param context
	 *            the BundleContext
	 * @return a list of reference or <code>null</code>
	 * @throws InvalidSyntaxException
	 *             if filter is not correct
	 */
	public ServiceReference<?>[] getServiceReferences(String clazz, String filter, BundleContext context) throws InvalidSyntaxException {
		if (context == null) {
			throw new NullPointerException("Context is null");
		}
		if (clazz == null && filter == null) {
			throw new NullPointerException("Either filter or clazz parameter should not be null");
		}
		try {
			return (ServiceReference[]) PrivilegedRunner.doPrivileged(controlContext, this, SERVICE_GET_REFERENCE, context, clazz, filter, null);
		} catch (InvalidSyntaxException e) {
			throw e;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Registers a service. Same as BundleContext.register(clazz, service,
	 * properties);
	 * 
	 * @param clazz
	 *            the class name of the service
	 * @param service
	 *            the service instance
	 * @param properties
	 *            the properties.
	 * @param context
	 *            the bundle context
	 * @return a service registration
	 */
	public ServiceRegistration<?> registerService(String clazz, Object service, Dictionary<?, ?> properties, BundleContext context) {
		if (context == null) {
			throw new NullPointerException("Context is null");
		}
		if (service == null) {
			throw new NullPointerException("Service is null");
		}
		if (clazz == null) {
			throw new NullPointerException("Class name is null");
		}
		try {
			return (ServiceRegistration<?>) PrivilegedRunner.doPrivileged(controlContext, this, SERVICE_REG_CLASS, context, clazz, service, properties);
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Registers a instance that implements many services. Same as
	 * BundleContext.register(clases, service, properties);
	 * 
	 * @param classes
	 *            the class names of the service
	 * @param service
	 *            the service instance
	 * @param properties
	 *            the properties.
	 * @param context
	 *            the bundle context
	 * @return a service registration
	 */
	public ServiceRegistration<?> registerService(String[] classes, Object service, Dictionary<?, ?> properties, BundleContext context) {
		if (context == null) {
			throw new NullPointerException("Context is null");
		}
		if (service == null) {
			throw new NullPointerException("Service is null");
		}
		if (classes == null) {
			throw new NullPointerException("Class names are null");
		}
		try {
			return (ServiceRegistration<?>) PrivilegedRunner.doPrivileged(controlContext, this, SERVICE_REG_CLASSES, context, classes, service, properties);
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Gets the location of the bundle. Same as Bundle.getLocation().
	 * 
	 * @param bundle
	 *            the bundle
	 * @return the bundle location
	 */
	public String getLocation(Bundle bundle) {
		try {
			return (String) PrivilegedRunner.doPrivileged(controlContext, this, BUNDLE_GET_LOCATION, bundle, null, null, null);
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Gets the bundle headers. Same as Bundle.getHeaders().
	 * 
	 * @param bundle
	 *            the bundle
	 * @return the bundle location
	 */
	public Dictionary<?, ?> getHeaders(Bundle bundle) {
		try {
			return (Dictionary<?, ?>) PrivilegedRunner.doPrivileged(controlContext, this, BUNDLE_GET_HEADERS, bundle, null, null, null);
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Starts the bundle. Same as Bundle.start()
	 * 
	 * @param bundle
	 *            the bundle
	 * @throws BundleException
	 */
	public void start(Bundle bundle) throws BundleException {
		try {
			PrivilegedRunner.doPrivileged(controlContext, this, BUNDLE_START, bundle, null, null, null);
		} catch (BundleException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Stops the bundle. Same as Bundle.stop()
	 * 
	 * @param bundle
	 *            the bundle
	 * @throws BundleException
	 */
	public void stop(Bundle bundle) throws BundleException {
		try {
			PrivilegedRunner.doPrivileged(controlContext, this, BUNDLE_STOP, bundle, null, null, null);
		} catch (BundleException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Un-installs the bundle. Same as Bundle.uninstall()
	 * 
	 * @param bundle
	 *            the bundle
	 * @throws BundleException
	 */
	public void uninstall(Bundle bundle) throws BundleException {
		try {
			PrivilegedRunner.doPrivileged(controlContext, this, BUNDLE_UNINSTALL, bundle, null, null, null);
		} catch (BundleException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Updates the bundle. Same as Bundle.update()
	 * 
	 * @param bundle
	 *            the bundle
	 * @throws BundleException
	 */
	public void update(Bundle bundle) throws BundleException {
		try {
			PrivilegedRunner.doPrivileged(controlContext, this, BUNDLE_UPDATE, bundle, null, null, null);
		} catch (BundleException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Updates the bundle from stream. Same as Bundle.update(stream)
	 * 
	 * @param bundle
	 *            the bundle
	 * @param is
	 *            the stream
	 * @throws BundleException
	 */
	public void update(Bundle bundle, InputStream is) throws BundleException {
		try {
			PrivilegedRunner.doPrivileged(controlContext, this, BUNDLE_UPDATE_IS, bundle, is, null, null);
		} catch (BundleException t) {
			throw t;
		} catch (Exception t) {
			throw new RuntimeException(t.getMessage());
		}
	}

	/**
	 * Performs a privileged action <b>using the current security context</b>.
	 * The method calls the dispatcher inside the privileged call passing it the
	 * same parameters that were passed to this method.
	 * 
	 * @param dispatcher
	 *            the dispatcher which should be called
	 * @param type
	 *            the type of the action - used in the dispatcher
	 * @param arg1
	 *            a parameter received by the dispatcher
	 * @param arg2
	 *            a parameter received by the dispatcher
	 * @param arg3
	 *            a parameter received by the dispatcher
	 * @param arg4
	 *            a parameter received by the dispatcher
	 * @return the object returned from the execution
	 * @throws Exception
	 *             if the dispatcher fails
	 * @see PrivilegedRunner#doPrivileged(Object,
	 *      org.eclipse.equinox.internal.util.security.PrivilegedRunner.PrivilegedDispatcher,
	 *      int, Object, Object, Object, Object)
	 */
	public Object doPrivileged(PrivilegedRunner.PrivilegedDispatcher dispatcher, int type, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception {
		return PrivilegedRunner.doPrivileged(controlContext, dispatcher, type, arg1, arg2, arg3, arg4);
	}

	/**
	 * Performs a privileged action <b>using the current security context</b>.
	 * The method calls the dispatcher inside the privileged call passing it the
	 * same parameters that were passed to this method.
	 * 
	 * @param dispatcher
	 *            the dispatcher which should be called
	 * @param type
	 *            the type of the action - used in the dispatcher
	 * @param arg1
	 *            a parameter received by the dispatcher
	 * @param arg2
	 *            a parameter received by the dispatcher
	 * @param arg3
	 *            a parameter received by the dispatcher
	 * @return the object returned from the execution
	 * @throws Exception
	 *             if the dispatcher fails
	 * @see PrivilegedRunner#doPrivileged(Object,
	 *      org.eclipse.equinox.internal.util.security.PrivilegedRunner.PrivilegedDispatcher,
	 *      int, Object, Object, Object, Object)
	 */
	public Object doPrivileged(PrivilegedRunner.PrivilegedDispatcher dispatcher, int type, Object arg1, Object arg2, Object arg3) throws Exception {
		return PrivilegedRunner.doPrivileged(controlContext, dispatcher, type, arg1, arg2, arg3, null);
	}

	/**
	 * Performs a privileged action <b>using the current security context</b>.
	 * The method calls the dispatcher inside the privileged call passing it the
	 * same parameters that were passed to this method.
	 * 
	 * @param dispatcher
	 *            the dispatcher which should be called
	 * @param type
	 *            the type of the action - used in the dispatcher
	 * @param arg1
	 *            a parameter received by the dispatcher
	 * @param arg2
	 *            a parameter received by the dispatcher
	 * @return the object returned from the execution
	 * @throws Exception
	 *             if the dispatcher fails
	 * @see PrivilegedRunner#doPrivileged(Object,
	 *      org.eclipse.equinox.internal.util.security.PrivilegedRunner.PrivilegedDispatcher,
	 *      int, Object, Object, Object, Object)
	 */
	public Object doPrivileged(PrivilegedRunner.PrivilegedDispatcher dispatcher, int type, Object arg1, Object arg2) throws Exception {
		return PrivilegedRunner.doPrivileged(controlContext, dispatcher, type, arg1, arg2, null, null);
	}

	/**
	 * Performs a privileged action <b>using the current security context</b>.
	 * The method calls the dispatcher inside the privileged call passing it the
	 * same parameters that were passed to this method.
	 * 
	 * @param dispatcher
	 *            the dispatcher which should be called
	 * @param type
	 *            the type of the action - used in the dispatcher
	 * @param arg1
	 *            a parameter received by the dispatcher
	 * @return the object returned from the execution
	 * @throws Exception
	 *             if the dispatcher fails
	 * @see PrivilegedRunner#doPrivileged(Object,
	 *      org.eclipse.equinox.internal.util.security.PrivilegedRunner.PrivilegedDispatcher,
	 *      int, Object, Object, Object, Object)
	 */
	public Object doPrivileged(PrivilegedRunner.PrivilegedDispatcher dispatcher, int type, Object arg1) throws Exception {
		return PrivilegedRunner.doPrivileged(controlContext, dispatcher, type, arg1, null, null, null);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Object dispatchPrivileged(int type, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception {
		switch (type) {
			case SYSTEM_GET_PROPERTY :
				return UtilActivator.bc.getProperty((String) arg1);
			case CREATE_THREAD :
				return new Thread((Runnable) arg1, (String) arg2);
			case CLASS_FOR_NAME :
				return Class.forName((String) arg1);
			case SYSTEM_SET_PROPERTY :
				return System.getProperties().put(arg1, arg2);
			case FILE_GET_INPUT_STREAM :
				return new FileInputStream((File) arg1);
			case FILE_GET_OUTPUT_STREAM :
				return new FileOutputStream(((File) arg1).getAbsolutePath(), ((Boolean) arg2).booleanValue());
			case FILE_LENGTH :
				return Long.valueOf(((File) arg1).length());
			case FILE_EXISTS :
				return ((File) arg1).exists() ? Boolean.TRUE : Boolean.FALSE;
			case FILE_ISDIR :
				return ((File) arg1).isDirectory() ? Boolean.TRUE : Boolean.FALSE;
			case FILE_LAST_MODIFIED :
				return Long.valueOf(((File) arg1).lastModified());
			case FILE_LIST :
				return ((File) arg1).list();
			case FILE_DELETE :
				return ((File) arg1).delete() ? Boolean.TRUE : Boolean.FALSE;
			case FILE_RENAME :
				return ((File) arg1).renameTo(((File) arg2)) ? Boolean.TRUE : Boolean.FALSE;
			case FILE_GET_RANDOM_ACCESS_FILE :
				return new RandomAccessFile((File) arg1, (String) arg2);
			case SERVICE_GET_REFERENCE :
				return ((BundleContext) arg1).getServiceReferences((String) arg2, (String) arg3);
			case SERVICE_GET_SERVICE :
				return ((BundleContext) arg1).getService((ServiceReference<?>) arg2);
			case SERVICE_REG_CLASS :
				return ((BundleContext) arg1).registerService((String) arg2, arg3, (Dictionary<String, ?>) arg4);
			case SERVICE_REG_CLASSES :
				return ((BundleContext) arg1).registerService((String[]) arg2, arg3, (Dictionary<String, ?>) arg4);
			case BUNDLE_GET_LOCATION :
				return ((Bundle) arg1).getLocation();
			case BUNDLE_GET_HEADERS :
				return ((Bundle) arg1).getHeaders();
			case BUNDLE_START :
				((Bundle) arg1).start();
				break;
			case BUNDLE_STOP :
				((Bundle) arg1).stop();
				break;
			case BUNDLE_UNINSTALL :
				((Bundle) arg1).uninstall();
				break;
			case BUNDLE_UPDATE :
				((Bundle) arg1).update();
				break;
			case BUNDLE_UPDATE_IS :
				((Bundle) arg1).update((InputStream) arg2);
				break;
		}
		return null;
	}

}
