Merge cedar with mozilla-central
authorMounir Lamouri <mounir.lamouri@gmail.com>
Tue, 19 Apr 2011 11:02:41 +0200
changeset 68297 bffd5f9dae2f6351abf3605cbbce110abe39811c
parent 68296 69d9c9392b2b1f0b9ed34bec446d211e27d7c4bf (current diff)
parent 68290 1c819504b0bbf65ad7c5260609ad6f07c89734ab (diff)
child 68298 a957251a4f4070761fd648405bcfd6dcddefe571
child 68344 150dcb7430d6c8947671d18e479a8565fb57418f
push id19576
push usermlamouri@mozilla.com
push dateTue, 19 Apr 2011 09:05:27 +0000
treeherdermozilla-central@bffd5f9dae2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone6.0a1
first release with
nightly win64
bffd5f9dae2f / 6.0a1 / 20110419030210 / files
nightly linux32
nightly linux64
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win64
Merge cedar with mozilla-central
config/config.mk
content/xslt/public/nsIXSLTProcessorObsolete.idl
js/src/config/config.mk
js/src/xpconnect/loader/mozJSLoaderConstructors.h
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcwrappednative.cpp
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -305,17 +305,17 @@ nsHyperTextAccessible::GetPosAndText(PRI
     return nsnull;
   }
 
   nsIFrame *startFrame = nsnull;
   if (aEndFrame) {
     *aEndFrame = nsnull;
   }
   if (aBoundsRect) {
-    aBoundsRect->Empty();
+    aBoundsRect->SetEmpty();
   }
   if (aStartAcc)
     *aStartAcc = nsnull;
   if (aEndAcc)
     *aEndAcc = nsnull;
 
   nsIntRect unionRect;
   nsAccessible *lastAccessible = nsnull;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3291,26 +3291,25 @@ const BrowserSearch = {
     if (window.location.href != getBrowserURL()) {
       var win = getTopWin();
       if (win) {
         // If there's an open browser window, it should handle this command
         win.focus()
         win.BrowserSearch.webSearch();
       } else {
         // If there are no open browser windows, open a new one
-
-        // This needs to be in a timeout so that we don't end up refocused
-        // in the url bar
-        function webSearchCallback() {
-          setTimeout(BrowserSearch.webSearch, 0);
+        function observer(subject, topic, data) {
+          if (subject == win) {
+            BrowserSearch.webSearch();
+            Services.obs.removeObserver(observer, "browser-delayed-startup-finished");
+          }
         }
-
         win = window.openDialog("chrome://browser/content/", "_blank",
                                 "chrome,all,dialog=no", "about:blank");
-        win.addEventListener("load", webSearchCallback, false);
+        Services.obs.addObserver(observer, "browser-delayed-startup-finished", false); 
       }
       return;
     }
 #endif
     var searchBar = this.searchBar;
     if (searchBar && window.fullScreen)
       FullScreen.mouseoverToggle(true);
 
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -67,17 +67,21 @@ DIRS += wince/tools \
 	$(DEPTH)/memory/mozalloc \
 	$(NULL)
 endif
 endif
 
 DIRS += pgo
 
 ifeq (Android,$(OS_TARGET))
-  DIRS += mobile/sutagent/android
+  DIRS += mobile/sutagent/android \
+          mobile/sutagent/android/watcher \
+          mobile/sutagent/android/ffxcp \
+          mobile/sutagent/android/fencp \
+          $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 # we install to _leaktest/
 TARGET_DEPTH = ..
 include $(srcdir)/automation-build.mk
 
--- a/build/mobile/sutagent/android/ASMozStub.java
+++ b/build/mobile/sutagent/android/ASMozStub.java
@@ -1,122 +1,215 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Android SUTAgent code.
- *
- * The Initial Developer of the Original Code is
- * Bob Moss.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Bob Moss <bmoss@mozilla.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package com.mozilla.SUTAgentAndroid.service;
-
-import java.net.ServerSocket;
-import java.util.Timer;
-
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.Intent;
-// import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.widget.Toast;
-
-public class ASMozStub extends android.app.Service {
-	
-	private ServerSocket cmdChnl = null;
-	private ServerSocket dataChnl = null;
-	private Handler handler = new Handler();
-	RunCmdThread runCmdThrd = null;
-	RunDataThread runDataThrd = null;
-	Thread monitor = null;
-	Timer timer = null;
-	
-	@Override
-	public IBinder onBind(Intent intent)
-		{
-		return null;
-		}
-	
-	@Override
-	public void onCreate() {
-		super.onCreate();
-		Toast.makeText(this, "Listener Service created...", Toast.LENGTH_LONG).show();
-		}
-
-	public void onStart(Intent intent, int startId) {
-		super.onStart(intent, startId);
-		
-		try {
-			cmdChnl = new ServerSocket(20701);
-			runCmdThrd = new RunCmdThread(cmdChnl, this, handler);
-			runCmdThrd.start();
-			Toast.makeText(this, "Command channel port 20701 ...", Toast.LENGTH_LONG).show();
-			
-			dataChnl = new ServerSocket(20700);
-			runDataThrd = new RunDataThread(dataChnl, this);
-			runDataThrd.start();
-			Toast.makeText(this, "Data channel port 20700 ...", Toast.LENGTH_LONG).show();
-			} 
-		catch (Exception e) {
-//			Toast.makeText(getApplication().getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
-			}
-		
-		return;
-		}
-	
-	public void onDestroy()
-		{
-		super.onDestroy();
-		if (runCmdThrd.isAlive())
-			{
-			runCmdThrd.StopListening();
-			}
-		
-		if (runDataThrd.isAlive())
-			{
-			runDataThrd.StopListening();
-			}
-		
-		NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
-		notificationManager.cancel(1959);
-		
-		Toast.makeText(this, "Listener Service destroyed...", Toast.LENGTH_LONG).show();
-		
-		System.exit(0);
-		}
-	
-	public void SendToDataChannel(String strToSend)
-		{
-		if (runDataThrd.isAlive())
-			{
-			runDataThrd.SendToDataChannel(strToSend);
-			}
-		}
-}
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android SUTAgent code.
+ *
+ * The Initial Developer of the Original Code is
+ * Bob Moss.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package com.mozilla.SUTAgentAndroid.service;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.ServerSocket;
+import java.util.Timer;
+
+import com.mozilla.SUTAgentAndroid.R;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Gravity;
+import android.widget.Toast;
+
+public class ASMozStub extends android.app.Service {
+
+    private ServerSocket cmdChnl = null;
+    private ServerSocket dataChnl = null;
+    private Handler handler = new Handler();
+    RunCmdThread runCmdThrd = null;
+    RunDataThread runDataThrd = null;
+    Thread monitor = null;
+    Timer timer = null;
+
+    @SuppressWarnings("unchecked")
+    private static final Class[] mStartForegroundSignature = new Class[] {
+        int.class, Notification.class};
+    @SuppressWarnings("unchecked")
+    private static final Class[] mStopForegroundSignature = new Class[] {
+        boolean.class};
+
+    private NotificationManager mNM;
+    private Method mStartForeground;
+    private Method mStopForeground;
+    private Object[] mStartForegroundArgs = new Object[2];
+    private Object[] mStopForegroundArgs = new Object[1];
+
+    @Override
+    public IBinder onBind(Intent intent)
+        {
+        return null;
+        }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+        try {
+            mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature);
+            mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature);
+            }
+        catch (NoSuchMethodException e) {
+            // Running on an older platform.
+            mStartForeground = mStopForeground = null;
+            }
+
+        doToast("Listener Service created...");
+        }
+
+    public void onStart(Intent intent, int startId) {
+        super.onStart(intent, startId);
+
+        try {
+            cmdChnl = new ServerSocket(20701);
+            runCmdThrd = new RunCmdThread(cmdChnl, this, handler);
+            runCmdThrd.start();
+            doToast("Command channel port 20701 ...");
+
+            dataChnl = new ServerSocket(20700);
+            runDataThrd = new RunDataThread(dataChnl, this);
+            runDataThrd.start();
+            doToast("Data channel port 20700 ...");
+
+            Notification notification = new Notification();
+            startForegroundCompat(R.string.foreground_service_started, notification);
+            }
+        catch (Exception e) {
+            doToast(e.toString());
+//            Toast.makeText(getApplication().getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
+            }
+
+        return;
+        }
+
+    public void onDestroy()
+        {
+        super.onDestroy();
+
+        if (runCmdThrd.isAlive())
+            {
+            runCmdThrd.StopListening();
+            }
+
+        if (runDataThrd.isAlive())
+            {
+            runDataThrd.StopListening();
+            }
+
+        NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+        notificationManager.cancel(1959);
+
+        stopForegroundCompat(R.string.foreground_service_started);
+
+        doToast("Listener Service destroyed...");
+
+        System.exit(0);
+        }
+
+    public void SendToDataChannel(String strToSend)
+        {
+        if (runDataThrd.isAlive())
+            runDataThrd.SendToDataChannel(strToSend);
+        }
+
+    public void doToast(String sMsg) {
+        Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG);
+        toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100);
+        toast.show();
+    }
+
+    /**
+     * This is a wrapper around the new startForeground method, using the older
+     * APIs if it is not available.
+     */
+    void startForegroundCompat(int id, Notification notification) {
+        // If we have the new startForeground API, then use it.
+        if (mStartForeground != null) {
+            mStartForegroundArgs[0] = Integer.valueOf(id);
+            mStartForegroundArgs[1] = notification;
+            try {
+                mStartForeground.invoke(this, mStartForegroundArgs);
+            } catch (InvocationTargetException e) {
+                // Should not happen.
+                Log.w("ScreenOnWidget", "Unable to invoke startForeground", e);
+            } catch (IllegalAccessException e) {
+                // Should not happen.
+                Log.w("ScreenOnWidget", "Unable to invoke startForeground", e);
+            }
+            return;
+        }
+
+        // Fall back on the old API.
+        setForeground(true);
+        mNM.notify(id, notification);
+    }
+
+    /**
+     * This is a wrapper around the new stopForeground method, using the older
+     * APIs if it is not available.
+     */
+    void stopForegroundCompat(int id) {
+        // If we have the new stopForeground API, then use it.
+        if (mStopForeground != null) {
+            mStopForegroundArgs[0] = Boolean.TRUE;
+            try {
+                mStopForeground.invoke(this, mStopForegroundArgs);
+            } catch (InvocationTargetException e) {
+                // Should not happen.
+                Log.w("ScreenOnWidget", "Unable to invoke stopForeground", e);
+            } catch (IllegalAccessException e) {
+                // Should not happen.
+                Log.w("ScreenOnWidget", "Unable to invoke stopForeground", e);
+            }
+            return;
+        }
+
+        // Fall back on the old API.  Note to cancel BEFORE changing the
+        // foreground state, since we could be killed at that point.
+        mNM.cancel(id);
+        setForeground(false);
+    }
+}
--- a/build/mobile/sutagent/android/AlertLooperThread.java
+++ b/build/mobile/sutagent/android/AlertLooperThread.java
@@ -1,96 +1,96 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Android SUTAgent code.
- *
- * The Initial Developer of the Original Code is
- * Bob Moss.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Bob Moss <bmoss@mozilla.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package com.mozilla.SUTAgentAndroid.service;
-
-import java.util.Timer;
-
-import android.content.ContextWrapper;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-
-class AlertLooperThread extends Thread
-	{
-	public Handler mHandler;
-	private Looper looper = null;
-	private DoAlert da	= null;
-	private Timer alertTimer = null;
-	private ContextWrapper contextWrapper = null;
-	
-	AlertLooperThread(ContextWrapper ctxW)
-		{
-		this.contextWrapper = ctxW;
-		}
-	
-	public Timer getAlertTimer()
-		{
-		return alertTimer;
-		}
-
-	public void term()
-		{
-		if (da != null)
-			da.term();
-		}
-
-	public void quit()
-		{
-		if (looper != null)
-			looper.quit();
-		}
-
-	public void run()
-		{
-		Looper.prepare();
-    
-		looper = Looper.myLooper();
-      
-		mHandler = new Handler()
-    		{
-			public void handleMessage(Message msg)
-        		{
-				// process incoming messages here
-        		}
-    		};
-      
-    	alertTimer = new Timer();
-    	da = new DoAlert(contextWrapper);
-    	alertTimer.scheduleAtFixedRate(da, 0, 5000);
-    	Looper.loop();
-		}
-	}
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android SUTAgent code.
+ *
+ * The Initial Developer of the Original Code is
+ * Bob Moss.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package com.mozilla.SUTAgentAndroid.service;
+
+import java.util.Timer;
+
+import android.content.ContextWrapper;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+class AlertLooperThread extends Thread
+    {
+    public Handler mHandler;
+    private Looper looper = null;
+    private DoAlert da    = null;
+    private Timer alertTimer = null;
+    private ContextWrapper contextWrapper = null;
+
+    AlertLooperThread(ContextWrapper ctxW)
+        {
+        this.contextWrapper = ctxW;
+        }
+
+    public Timer getAlertTimer()
+        {
+        return alertTimer;
+        }
+
+    public void term()
+        {
+        if (da != null)
+            da.term();
+        }
+
+    public void quit()
+        {
+        if (looper != null)
+            looper.quit();
+        }
+
+    public void run()
+        {
+        Looper.prepare();
+
+        looper = Looper.myLooper();
+
+        mHandler = new Handler()
+            {
+            public void handleMessage(Message msg)
+                {
+                // process incoming messages here
+                }
+            };
+
+        alertTimer = new Timer();
+        da = new DoAlert(contextWrapper);
+        alertTimer.scheduleAtFixedRate(da, 0, 5000);
+        Looper.loop();
+        }
+    }
--- a/build/mobile/sutagent/android/AndroidManifest.xml
+++ b/build/mobile/sutagent/android/AndroidManifest.xml
@@ -1,13 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.mozilla.SUTAgentAndroid"
-      android:versionCode="1"
-      android:versionName="1.0" android:sharedUserId="org.mozilla.sharedID">
+      android:versionCode="1" android:versionName="1.01">
     <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
         <activity android:name=".SUTAgentAndroid"
                   android:screenOrientation="nosensor"
                   android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
@@ -18,18 +17,17 @@
                  <category android:value="android.intent.category.HOME" android:name="android.intent.category.HOME"/>
             </intent-filter>
         </receiver>
         <service android:name=".service.ASMozStub">
 		    <intent-filter>
 			    <action android:name="com.mozilla.SUTAgentAndroid.service.LISTENER_SERVICE" />
 		    </intent-filter>
 	    </service>
-
-    </application>
+	</application>
     
     <uses-sdk android:minSdkVersion="5" android:targetSdkVersion="8"/>
 
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
 <uses-permission android:name="android.permission.INTERNET"></uses-permission>
 <uses-permission android:name="android.permission.REBOOT"></uses-permission>
 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
@@ -38,34 +36,22 @@
 <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
 <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses-permission>
 <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"></uses-permission>
 <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
 <uses-permission android:name="android.permission.BATTERY_STATS"></uses-permission>
 <uses-permission android:name="android.permission.DEVICE_POWER"></uses-permission>
 <uses-permission android:name="android.permission.DISABLE_KEYGUARD"></uses-permission>
 <uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
-
 <uses-permission android:name="android.permission.WRITE_SETTINGS"></uses-permission>
 <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"></uses-permission>
-
 <uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>
-
 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
-
 <uses-permission android:name="android.permission.INSTALL_PACKAGES"></uses-permission>
-
-
-
-
 <uses-permission android:name="android.permission.STATUS_BAR"></uses-permission>
-
 <uses-permission android:name="android.permission.VIBRATE"></uses-permission>
-
-
-
 <uses-permission android:name="android.permission.SET_TIME"></uses-permission>
-
-
-
 <uses-permission android:name="android.permission.SET_TIME_ZONE"></uses-permission>
 
+<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"></uses-permission>
+<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"></uses-permission>
+
 </manifest> 
\ No newline at end of file
--- a/build/mobile/sutagent/android/CmdWorkerThread.java
+++ b/build/mobile/sutagent/android/CmdWorkerThread.java
@@ -1,199 +1,199 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Android SUTAgent code.
- *
- * The Initial Developer of the Original Code is
- * Bob Moss.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Bob Moss <bmoss@mozilla.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package com.mozilla.SUTAgentAndroid.service;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-
-// import com.mozilla.SUTAgentAndroid.DoCommand;
-
-public class CmdWorkerThread extends Thread
-{
-	private RunCmdThread theParent = null;
-	private Socket socket	= null;
-	private String prompt = null;
-	boolean bListening	= true;
-
-	public CmdWorkerThread(RunCmdThread theParent, Socket workerSocket)
-		{
-		super("CmdWorkerThread");
-		this.theParent = theParent;
-		this.socket = workerSocket;
-		byte pr [] = new byte [3];
-		pr[0] = '$';
-		pr[1] = '>';
-		pr[2] = 0;
-		prompt = new String(pr,0,3);
-		}
-
-	public void StopListening()
-		{
-		bListening = false;
-		}
-	
-	private String readLine(BufferedInputStream in)
-		{
-		String sRet = "";
-		int nByte = 0;
-		char cChar = 0;
-		
-		try 
-			{
-			nByte = in.read();
-			while (nByte != -1)
-				{
-				cChar = ((char)(nByte & 0xFF));
-				if ((cChar != '\r') && (cChar != '\n'))
-					sRet += cChar;
-				else
-					break;
-				nByte = in.read();
-				}
-			
-			if ((in.available() > 0) && (cChar != '\n'))
-				{
-				in.mark(1024);
-				nByte = in.read();
-				
-				if (nByte != -1)
-					{
-					cChar = ((char)(nByte & 0xFF));
-					if (cChar != '\n')
-						{
-						in.reset();
-						}
-					}
-				}
-			}
-		catch (IOException e)
-			{
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-			}
-		
-		if (sRet.length() == 0)
-			sRet = null;
-		
-		return(sRet);
-		}
-
-	public void run()
-		{
-		try {
-			OutputStream cmdOut = socket.getOutputStream();
-			InputStream cmdIn = socket.getInputStream();
-			PrintWriter out = new PrintWriter(cmdOut, true);
-			BufferedInputStream in = new BufferedInputStream(cmdIn);
-			String inputLine, outputLine;
-			DoCommand dc = new DoCommand(theParent.svc);
-
-			int nAvail = cmdIn.available();
-			cmdIn.skip(nAvail);
-				
-			out.print(prompt);
-			out.flush();
-
-			while (bListening)
-				{
-				if (!(in.available() > 0))
-					{
-					socket.setSoTimeout(500);
-					try {
-						int nRead = cmdIn.read();
-						if (nRead == -1)
-							{
-							bListening = false;
-							continue;
-							}
-						else
-							{
-							inputLine = ((char)nRead) + "";
-							socket.setSoTimeout(120000);
-							}
-						}
-					catch(SocketTimeoutException toe)
-						{
-						continue;
-						}
-					}
-				else
-					inputLine = "";
-				
-				if ((inputLine += readLine(in)) != null)
-					{
-					outputLine = dc.processCommand(inputLine, out, in, cmdOut);
-					if (outputLine.length() > 0)
-						{
-						out.print(outputLine + "\n" + prompt);
-						}
-					else
-						out.print(prompt);
-					out.flush();
-					if (outputLine.equals("exit"))
-						{
-						theParent.StopListening();
-						bListening = false;
-						}
-					if (outputLine.equals("quit"))
-						{
-						bListening = false;
-						}
-					outputLine = null;
-					System.gc();
-					}
-				else
-					break;
-				}
-			out.close();
-			out = null;
-			in.close();
-			in = null;
-			socket.close();
-		}
-	catch (IOException e)
-		{
-		// TODO Auto-generated catch block
-		e.printStackTrace();
-		}
-	}
-}
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android SUTAgent code.
+ *
+ * The Initial Developer of the Original Code is
+ * Bob Moss.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package com.mozilla.SUTAgentAndroid.service;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+
+// import com.mozilla.SUTAgentAndroid.DoCommand;
+
+public class CmdWorkerThread extends Thread
+{
+    private RunCmdThread theParent = null;
+    private Socket socket    = null;
+    private String prompt = null;
+    boolean bListening    = true;
+
+    public CmdWorkerThread(RunCmdThread theParent, Socket workerSocket)
+        {
+        super("CmdWorkerThread");
+        this.theParent = theParent;
+        this.socket = workerSocket;
+        byte pr [] = new byte [3];
+        pr[0] = '$';
+        pr[1] = '>';
+        pr[2] = 0;
+        prompt = new String(pr,0,3);
+        }
+
+    public void StopListening()
+        {
+        bListening = false;
+        }
+
+    private String readLine(BufferedInputStream in)
+        {
+        String sRet = "";
+        int nByte = 0;
+        char cChar = 0;
+
+        try
+            {
+            nByte = in.read();
+            while (nByte != -1)
+                {
+                cChar = ((char)(nByte & 0xFF));
+                if ((cChar != '\r') && (cChar != '\n'))
+                    sRet += cChar;
+                else
+                    break;
+                nByte = in.read();
+                }
+
+            if ((in.available() > 0) && (cChar != '\n'))
+                {
+                in.mark(1024);
+                nByte = in.read();
+
+                if (nByte != -1)
+                    {
+                    cChar = ((char)(nByte & 0xFF));
+                    if (cChar != '\n')
+                        {
+                        in.reset();
+                        }
+                    }
+                }
+            }
+        catch (IOException e)
+            {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+            }
+
+        if (sRet.length() == 0)
+            sRet = null;
+
+        return(sRet);
+        }
+
+    public void run()
+        {
+        try {
+            OutputStream cmdOut = socket.getOutputStream();
+            InputStream cmdIn = socket.getInputStream();
+            PrintWriter out = new PrintWriter(cmdOut, true);
+            BufferedInputStream in = new BufferedInputStream(cmdIn);
+            String inputLine, outputLine;
+            DoCommand dc = new DoCommand(theParent.svc);
+
+            int nAvail = cmdIn.available();
+            cmdIn.skip(nAvail);
+
+            out.print(prompt);
+            out.flush();
+
+            while (bListening)
+                {
+                if (!(in.available() > 0))
+                    {
+                    socket.setSoTimeout(500);
+                    try {
+                        int nRead = cmdIn.read();
+                        if (nRead == -1)
+                            {
+                            bListening = false;
+                            continue;
+                            }
+                        else
+                            {
+                            inputLine = ((char)nRead) + "";
+                            socket.setSoTimeout(120000);
+                            }
+                        }
+                    catch(SocketTimeoutException toe)
+                        {
+                        continue;
+                        }
+                    }
+                else
+                    inputLine = "";
+
+                if ((inputLine += readLine(in)) != null)
+                    {
+                    outputLine = dc.processCommand(inputLine, out, in, cmdOut);
+                    if (outputLine.length() > 0)
+                        {
+                        out.print(outputLine + "\n" + prompt);
+                        }
+                    else
+                        out.print(prompt);
+                    out.flush();
+                    if (outputLine.equals("exit"))
+                        {
+                        theParent.StopListening();
+                        bListening = false;
+                        }
+                    if (outputLine.equals("quit"))
+                        {
+                        bListening = false;
+                        }
+                    outputLine = null;
+                    System.gc();
+                    }
+                else
+                    break;
+                }
+            out.close();
+            out = null;
+            in.close();
+            in = null;
+            socket.close();
+        }
+    catch (IOException e)
+        {
+        // TODO Auto-generated catch block
+        e.printStackTrace();
+        }
+    }
+}
--- a/build/mobile/sutagent/android/DataWorkerThread.java
+++ b/build/mobile/sutagent/android/DataWorkerThread.java
@@ -1,237 +1,237 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Android SUTAgent code.
- *
- * The Initial Developer of the Original Code is
- * Bob Moss.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Bob Moss <bmoss@mozilla.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package com.mozilla.SUTAgentAndroid.service;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-
-// import com.mozilla.SUTAgentAndroid.DoCommand;
-import com.mozilla.SUTAgentAndroid.SUTAgentAndroid;
-
-public class DataWorkerThread extends Thread
-{
-	private RunDataThread theParent = null;
-	private Socket socket	= null;
-	boolean bListening	= true;
-	PrintWriter out = null;
-	SimpleDateFormat sdf = null;
-
-	public DataWorkerThread(RunDataThread theParent, Socket workerSocket)
-		{
-		super("DataWorkerThread");
-		this.theParent = theParent;
-		this.socket = workerSocket;
-		this.sdf = new SimpleDateFormat("yyyyMMdd-HH:mm:ss");
-		}
-
-	public void StopListening()
-		{
-		bListening = false;
-		}
-	
-	public void SendString(String strToSend)
-		{
-		if (this.out != null)
-			{
-			Calendar cal = Calendar.getInstance();
-			String strOut = sdf.format(cal.getTime());
-			strOut += " " + strToSend + "\r\n";
-		
-			out.write(strOut);
-			out.flush();
-			}
-		}
-
-	private String readLine(BufferedInputStream in)
-		{
-		String sRet = "";
-		int nByte = 0;
-		char cChar = 0;
-	
-		try 
-			{
-			nByte = in.read();
-			while (nByte != -1)
-				{
-				cChar = ((char)(nByte & 0xFF));
-				if ((cChar != '\r') && (cChar != '\n'))
-					sRet += cChar;
-				else
-					break;
-				nByte = in.read();
-				}
-		
-			if (in.available() > 0)
-				{
-				in.mark(1024);
-				nByte = in.read();
-		
-				while (nByte != -1)
-					{
-					cChar = ((char)(nByte & 0xFF));
-					if ((cChar == '\r') || (cChar == '\n'))
-						{
-						if (in.available() > 0)
-							{
-							in.mark(1024);
-							nByte = in.read();
-							}
-						else
-							nByte = -1;
-						}
-					else
-						{
-						in.reset();
-						break;
-						}
-					}
-				}
-			}
-		catch (IOException e)
-			{
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-			}
-	
-		if (sRet.length() == 0)
-			sRet = null;
-	
-		return(sRet);
-		}
-
-	public void run()
-		{
-		String	sRet = "";
-		long lEndTime = System.currentTimeMillis() + 60000;
-		
-		try {
-			while(bListening)
-				{
-				OutputStream cmdOut = socket.getOutputStream();
-				InputStream cmdIn = socket.getInputStream();
-				this.out = new PrintWriter(cmdOut, true);
-				BufferedInputStream in = new BufferedInputStream(cmdIn);
-				String inputLine, outputLine;
-				DoCommand dc = new DoCommand(theParent.svc);
-				
-	    		Calendar cal = Calendar.getInstance();
-	    		sRet = sdf.format(cal.getTime());
-	    		sRet += " trace output";
-
-				out.println(sRet);
-				out.flush();
-				int nAvail = cmdIn.available();
-				cmdIn.skip(nAvail);
-				
-				while (bListening)
-					{
-					if (System.currentTimeMillis() > lEndTime)
-						{
-						cal = Calendar.getInstance();
-			    		sRet = sdf.format(cal.getTime());
-			    		sRet += " Thump thump - " + SUTAgentAndroid.sUniqueID + "\r\n";
-
-			    		out.write(sRet);
-			    		out.flush();
-						
-						lEndTime = System.currentTimeMillis() + 60000;
-						}
-					
-					if (!(in.available() > 0))
-						{
-						socket.setSoTimeout(500);
-						try {
-							int nRead = cmdIn.read();
-							if (nRead == -1)
-								{
-								bListening = false;
-								continue;
-								}
-							else
-								inputLine = (char)nRead + ""; 
-							}
-						catch(SocketTimeoutException toe)
-							{
-							continue;
-							}
-						}
-					else
-						inputLine = "";
-				
-					if ((inputLine += readLine(in)) != null)
-						{
-						outputLine = dc.processCommand(inputLine, out, in, cmdOut);
-						out.print(outputLine + "\n");
-						out.flush();
-						if (outputLine.equals("exit"))
-							{
-							theParent.StopListening();
-							bListening = false;
-							}
-						if (outputLine.equals("quit"))
-							{
-							bListening = false;
-							}
-						outputLine = null;
-						System.gc();
-						}
-					else
-						break;
-					}
-				
-				out.close();
-				out = null;
-				in.close();
-				in = null;
-				socket.close();
-				}
-			}
-		catch (IOException e)
-			{
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-			}
-		}
-}
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android SUTAgent code.
+ *
+ * The Initial Developer of the Original Code is
+ * Bob Moss.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package com.mozilla.SUTAgentAndroid.service;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+// import com.mozilla.SUTAgentAndroid.DoCommand;
+import com.mozilla.SUTAgentAndroid.SUTAgentAndroid;
+
+public class DataWorkerThread extends Thread
+{
+    private RunDataThread theParent = null;
+    private Socket socket    = null;
+    boolean bListening    = true;
+    PrintWriter out = null;
+    SimpleDateFormat sdf = null;
+
+    public DataWorkerThread(RunDataThread theParent, Socket workerSocket)
+        {
+        super("DataWorkerThread");
+        this.theParent = theParent;
+        this.socket = workerSocket;
+        this.sdf = new SimpleDateFormat("yyyyMMdd-HH:mm:ss");
+        }
+
+    public void StopListening()
+        {
+        bListening = false;
+        }
+
+    public void SendString(String strToSend)
+        {
+        if (this.out != null)
+            {
+            Calendar cal = Calendar.getInstance();
+            String strOut = sdf.format(cal.getTime());
+            strOut += " " + strToSend + "\r\n";
+
+            out.write(strOut);
+            out.flush();
+            }
+        }
+
+    private String readLine(BufferedInputStream in)
+        {
+        String sRet = "";
+        int nByte = 0;
+        char cChar = 0;
+
+        try
+            {
+            nByte = in.read();
+            while (nByte != -1)
+                {
+                cChar = ((char)(nByte & 0xFF));
+                if ((cChar != '\r') && (cChar != '\n'))
+                    sRet += cChar;
+                else
+                    break;
+                nByte = in.read();
+                }
+
+            if (in.available() > 0)
+                {
+                in.mark(1024);
+                nByte = in.read();
+
+                while (nByte != -1)
+                    {
+                    cChar = ((char)(nByte & 0xFF));
+                    if ((cChar == '\r') || (cChar == '\n'))
+                        {
+                        if (in.available() > 0)
+                            {
+                            in.mark(1024);
+                            nByte = in.read();
+                            }
+                        else
+                            nByte = -1;
+                        }
+                    else
+                        {
+                        in.reset();
+                        break;
+                        }
+                    }
+                }
+            }
+        catch (IOException e)
+            {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+            }
+
+        if (sRet.length() == 0)
+            sRet = null;
+
+        return(sRet);
+        }
+
+    public void run()
+        {
+        String    sRet = "";
+        long lEndTime = System.currentTimeMillis() + 60000;
+
+        try {
+            while(bListening)
+                {
+                OutputStream cmdOut = socket.getOutputStream();
+                InputStream cmdIn = socket.getInputStream();
+                this.out = new PrintWriter(cmdOut, true);
+                BufferedInputStream in = new BufferedInputStream(cmdIn);
+                String inputLine, outputLine;
+                DoCommand dc = new DoCommand(theParent.svc);
+
+                Calendar cal = Calendar.getInstance();
+                sRet = sdf.format(cal.getTime());
+                sRet += " trace output";
+
+                out.println(sRet);
+                out.flush();
+                int nAvail = cmdIn.available();
+                cmdIn.skip(nAvail);
+
+                while (bListening)
+                    {
+                    if (System.currentTimeMillis() > lEndTime)
+                        {
+                        cal = Calendar.getInstance();
+                        sRet = sdf.format(cal.getTime());
+                        sRet += " Thump thump - " + SUTAgentAndroid.sUniqueID + "\r\n";
+
+                        out.write(sRet);
+                        out.flush();
+
+                        lEndTime = System.currentTimeMillis() + 60000;
+                        }
+
+                    if (!(in.available() > 0))
+                        {
+                        socket.setSoTimeout(500);
+                        try {
+                            int nRead = cmdIn.read();
+                            if (nRead == -1)
+                                {
+                                bListening = false;
+                                continue;
+                                }
+                            else
+                                inputLine = (char)nRead + "";
+                            }
+                        catch(SocketTimeoutException toe)
+                            {
+                            continue;
+                            }
+                        }
+                    else
+                        inputLine = "";
+
+                    if ((inputLine += readLine(in)) != null)
+                        {
+                        outputLine = dc.processCommand(inputLine, out, in, cmdOut);
+                        out.print(outputLine + "\n");
+                        out.flush();
+                        if (outputLine.equals("exit"))
+                            {
+                            theParent.StopListening();
+                            bListening = false;
+                            }
+                        if (outputLine.equals("quit"))
+                            {
+                            bListening = false;
+                            }
+                        outputLine = null;
+                        System.gc();
+                        }
+                    else
+                        break;
+                    }
+
+                out.close();
+                out = null;
+                in.close();
+                in = null;
+                socket.close();
+                }
+            }
+        catch (IOException e)
+            {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+            }
+        }
+}
--- a/build/mobile/sutagent/android/DoAlert.java
+++ b/build/mobile/sutagent/android/DoAlert.java
@@ -1,81 +1,81 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Android SUTAgent code.
- *
- * The Initial Developer of the Original Code is
- * Bob Moss.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Bob Moss <bmoss@mozilla.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package com.mozilla.SUTAgentAndroid.service;
-
-import java.util.TimerTask;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.widget.Toast;
-
-class DoAlert extends TimerTask
-	{
-	int	lcv = 0;
-	Toast toast = null;
-	Ringtone rt = null;
-
-	DoAlert(ContextWrapper contextWrapper)
-		{
-		Context	ctx = contextWrapper.getApplicationContext();
-		this.toast = Toast.makeText(ctx, "Help me!", Toast.LENGTH_LONG);
-		rt = RingtoneManager.getRingtone(ctx, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM));
-		}
-
-	public void term()
-		{
-		if (rt != null)
-			{
-			if (rt.isPlaying())
-				rt.stop();
-			}
-
-		if (toast != null)
-			toast.cancel();
-		}
-
-	public void run ()
-		{
-		String sText =(((lcv++ % 2) == 0)  ? "Help me!" : "I've fallen down!" );
-		toast.setText(sText);
-		toast.show();
-		if (rt != null)
-			rt.play();
-		}
-	}
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android SUTAgent code.
+ *
+ * The Initial Developer of the Original Code is
+ * Bob Moss.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package com.mozilla.SUTAgentAndroid.service;
+
+import java.util.TimerTask;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.widget.Toast;
+
+class DoAlert extends TimerTask
+    {
+    int    lcv = 0;
+    Toast toast = null;
+    Ringtone rt = null;
+
+    DoAlert(ContextWrapper contextWrapper)
+        {
+        Context    ctx = contextWrapper.getApplicationContext();
+        this.toast = Toast.makeText(ctx, "Help me!", Toast.LENGTH_LONG);
+        rt = RingtoneManager.getRingtone(ctx, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM));
+        }
+
+    public void term()
+        {
+        if (rt != null)
+            {
+            if (rt.isPlaying())
+                rt.stop();
+            }
+
+        if (toast != null)
+            toast.cancel();
+        }
+
+    public void run ()
+        {
+        String sText =(((lcv++ % 2) == 0)  ? "Help me!" : "I've fallen down!" );
+        toast.setText(sText);
+        toast.show();
+        if (rt != null)
+            rt.play();
+        }
+    }
--- a/build/mobile/sutagent/android/DoCommand.java
+++ b/build/mobile/sutagent/android/DoCommand.java
@@ -1,2967 +1,3681 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Android SUTAgent code.
- *
- * The Initial Developer of the Original Code is
- * Bob Moss.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Bob Moss <bmoss@mozilla.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package com.mozilla.SUTAgentAndroid.service;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.lang.reflect.Field;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.Timer;
-import java.util.zip.Adler32;
-import java.util.zip.CheckedInputStream;
-import java.util.zip.CheckedOutputStream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
-import java.util.zip.ZipOutputStream;
-
-import org.apache.commons.net.ftp.FTP;
-import org.apache.commons.net.ftp.FTPClient;
-import org.apache.commons.net.ftp.FTPFile;
-import org.apache.commons.net.ftp.FTPReply;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-
-import com.mozilla.SUTAgentAndroid.SUTAgentAndroid;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Debug;
-import android.os.Environment;
-import android.os.StatFs;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.WindowManager;
-
-public class DoCommand {
-	
-	String lineSep = System.getProperty("line.separator");
-	Process	pProc;
-	OutputStream sutIn;
-	InputStream	sutErr;
-	InputStream	sutOut;
-	AlertLooperThread alrt = null;
-	ContextWrapper	contextWrapper = null;
-	
-	String	currentDir = "/";
-	String	sErrorPrefix = "##AGENT-WARNING## ";
-	
-	private final String prgVersion = "SUTAgentAndroid Version 0.87";
-	
-	public enum Command
-		{
-		RUN ("run"),
-		EXEC ("exec"),
-		ENVRUN ("envrun"),
-		KILL ("kill"),
-		PS ("ps"),
-		DEVINFO ("info"),
-		OS ("os"),
-		ID ("id"),
-		UPTIME ("uptime"),
-		SETTIME ("settime"),
-		SYSTIME ("systime"),
-		SCREEN ("screen"),
-		MEMORY ("memory"),
-		POWER ("power"),
-		PROCESS ("process"),
-		GETAPPROOT ("getapproot"),
-		TESTROOT ("testroot"),
-		ALRT ("alrt"),
-		DISK ("disk"),
-		CP ("cp"),
-		TIME ("time"),
-		HASH ("hash"),
-		CD ("cd"),
-		CAT ("cat"),
-		CWD ("cwd"),
-		MV ("mv"),
-		PUSH ("push"),
-		RM ("rm"),
-		PRUNE ("rmdr"),
-		MKDR ("mkdr"),
-		DIRWRITABLE ("dirw"),
-		DEAD ("dead"),
-		MEMS ("mems"),
-		LS ("ls"),
-		TMPD ("tmpd"),
-		PING ("ping"),
-		REBT ("rebt"),
-		UNZP ("unzp"),
-		ZIP ("zip"),
-		CLOK ("clok"),
-		STAT ("stat"),
-		QUIT ("quit"),
-		EXIT ("exit"),
-		HELP ("help"),
-		FTPG ("ftpg"),
-		FTPP ("ftpp"),
-		INST ("inst"),
-		UPDT ("updt"),
-		UNINST ("uninst"),
-		TEST ("test"),
-		VER ("ver"),
-		TZGET ("tzget"),
-		TZSET ("tzset"),
-		UNKNOWN ("unknown");
-		
-		private final String theCmd;
-		
-		Command(String theCmd) { this.theCmd = theCmd; }
-
-		public String theCmd() {return theCmd;}
-		
-		public static Command getCmd(String sCmd)
-			{
-			Command retCmd = UNKNOWN;
-			for (Command cmd : Command.values())
-				{
-				if (cmd.theCmd().equalsIgnoreCase(sCmd))
-					{
-					retCmd = cmd;
-					break;
-					}
-				}
-			return (retCmd);
-			}
-		}
-	
-	public DoCommand(ContextWrapper service)
-		{
-		this.contextWrapper = service;
-		}
-	
-	public String processCommand(String theCmdLine, PrintWriter out, BufferedInputStream in, OutputStream cmdOut)
-		{
-		String 	strReturn = "";
-		Command	cCmd = null;
-		Command cSubCmd = null;
-		
-		String [] Argv = parseCmdLine2(theCmdLine);
-		
-		int Argc = Argv.length;
-		
-		cCmd = Command.getCmd(Argv[0]);
-		
-		switch(cCmd)
-			{
-			case VER:
-				strReturn = prgVersion;
-				break;
-				
-			case CLOK:
-				strReturn = GetClok();
-				break;
-				
-			case TZGET:
-				strReturn = GetTimeZone();
-				break;
-				
-			case TZSET:
-				if (Argc == 2)
-					strReturn = SetTimeZone(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for settz command!";
-				break;
-				
-			case UPDT:
-				strReturn = StartUpdateOMatic(Argv[1], Argv[2]);
-				break;
-			
-			case SETTIME:
-				strReturn = SetSystemTime(Argv[1], Argv[2], cmdOut);
-				break;
-			
-			case CWD:
-				try {
-					strReturn = new java.io.File(currentDir).getCanonicalPath();
-					} 
-				catch (IOException e)
-					{
-					e.printStackTrace();
-					}
-				break;
-				
-			case CD:
-				if (Argc == 2)
-					strReturn = changeDir(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for cd command!";
-				break;
-			
-			case LS:
-				strReturn = PrintDir(((Argc > 1) ? Argv[1] : currentDir));
-				break;
-				
-			case GETAPPROOT:
-				if (Argc == 2)
-					strReturn = GetAppRoot(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for getapproot command!";
-				break;
-				
-			case TESTROOT:
-				strReturn = GetTestRoot();
-				break;
-				
-			case DEAD:
-				if (Argc == 2)
-					strReturn = (IsProcessDead(Argv[1]) ? (Argv[1] + " is hung or unresponsive") : (Argv[1] + " is ok"));
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for dead command!";
-				break;
-				
-			case PS:
-				strReturn = GetProcessInfo();
-				break;
-				
-			case PUSH:
-				if (Argc == 3)
-					{
-					long lArg = 0;
-				    try
-				    	{
-				        lArg = Long.parseLong(Argv[2].trim());
-				        System.out.println("long l = " + lArg);
-				    	}
-				    catch (NumberFormatException nfe)
-				    	{
-				        System.out.println("NumberFormatException: " + nfe.getMessage());
-				    	}
-					
-					strReturn = Push(Argv[1], in, lArg);
-					}
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for push command!";
-				break;
-				
-			case INST:
-				if (Argc >= 2)
-					strReturn = InstallApp(Argv[1], cmdOut);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for inst command!";
-				break;
-				
-			case UNINST:
-				if (Argc >= 2)
-					strReturn = UnInstallApp(Argv[1], cmdOut);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for inst command!";
-				break;
-				
-			case ALRT:
-				if (Argc == 2)
-					{
-					if (Argv[1].contentEquals("on"))
-						{
-						StartAlert();
-						}
-					else
-						{
-						StopAlert();
-						}
-					}
-				else
-					{
-					strReturn = sErrorPrefix + "Wrong number of arguments for alrt command!";
-					}
-				break;
-				
-			case REBT:
-				RunReboot(cmdOut);
-				break;
-				
-			case TMPD:
-				strReturn = GetTmpDir();
-				break;
-				
-			case DEVINFO:
-				if (Argc == 1)
-					{
-					strReturn += SUTAgentAndroid.sUniqueID;
-					strReturn += "\n";
-					strReturn += GetOSInfo();
-					strReturn += "\n";
-					strReturn += GetSystemTime();
-					strReturn += "\n";
-					strReturn += GetUptime();
-					strReturn += "\n";
-					strReturn += GetScreenInfo();
-					strReturn += "\n";
-					strReturn += GetMemoryInfo();
-					strReturn += "\n";
-					strReturn += GetPowerInfo();
-					strReturn += "\n";
-					strReturn += GetProcessInfo();
-					}
-				else
-					{
-					cSubCmd = Command.getCmd(Argv[1]);
-					switch(cSubCmd)
-						{
-						case ID:
-							strReturn = SUTAgentAndroid.sUniqueID;
-							break;
-							
-						case SCREEN:
-							strReturn = GetScreenInfo();
-							break;
-							
-						case PROCESS:
-							strReturn = GetProcessInfo();
-							break;
-							
-						case OS:
-							strReturn = GetOSInfo();
-							break;
-							
-						case SYSTIME:
-							strReturn = GetSystemTime();
-							break;
-							
-						case UPTIME:
-							strReturn = GetUptime();
-							break;
-							
-						case MEMORY:
-							strReturn = GetMemoryInfo();
-							break;
-							
-						case POWER:
-							strReturn += GetPowerInfo();
-							break;
-							
-						default:
-							break;
-						}
-					}
-				break;
-				
-			case STAT:
-				if (Argc == 2)
-					strReturn = StatProcess(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for ping command!";
-				break;
-				
-			case PING:
-				if (Argc == 2)
-					strReturn = SendPing(Argv[1], cmdOut);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for ping command!";
-				break;
-				
-			case HASH:
-				if (Argc == 2)
-					strReturn = HashFile(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for hash command!";
-				break;
-				
-			case PRUNE:
-				if (Argc == 2)
-					strReturn = PruneDir(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for prune command!";
-				break;
-				
-			case FTPG:
-				if (Argc == 4)
-					strReturn = FTPGetFile(Argv[1], Argv[2], Argv[3], cmdOut);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for ftpg command!";
-				break;
-				
-			case CAT:
-				if (Argc == 2)
-					strReturn = Cat(Argv[1], cmdOut);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for cat command!";
-				break;
-				
-			case DIRWRITABLE:
-				if (Argc == 2)
-					strReturn = IsDirWritable(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for dirwritable command!";
-				break;
-				
-			case TIME:
-				if (Argc == 2)
-					strReturn = PrintFileTimestamp(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for time command!";
-				break;
-				
-			case MKDR:
-				if (Argc == 2)
-					strReturn = MakeDir(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for mkdr command!";
-				break;
-				
-			case RM:
-				if (Argc == 2)
-					strReturn = RemoveFile(Argv[1]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for mkdr command!";
-				break;
-				
-			case MV:
-				if (Argc == 3)
-					strReturn = Move(Argv[1], Argv[2]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for mv command!";
-				break;
-				
-			case CP:
-				if (Argc == 3)
-					strReturn = CopyFile(Argv[1], Argv[2]);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for cp command!";
-				break;
-				
-			case QUIT:
-			case EXIT:
-				strReturn = Argv[0];
-				break;
-				
-			case TEST:
-//				boolean bRet = false;
-/*				
-				Configuration userConfig = new Configuration();
-				Settings.System.getConfiguration( contextWrapper.getContentResolver(), userConfig );
-				Calendar cal = Calendar.getInstance( userConfig.locale);
-				TimeZone ctz = cal.getTimeZone();
-				String sctzLongName = ctz.getDisplayName();
-				String pstzName = TimeZone.getDefault().getDisplayName();
-*/
-				String sTimeZoneName = GetTimeZone();
-				
-				TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
-				TimeZone tz2 = TimeZone.getTimeZone("GMT-08:00");
-				int	nOffset = (-8 * 3600000);
-				String [] zoneNames = TimeZone.getAvailableIDs(nOffset);
-				int nNumMatches = zoneNames.length;
-				TimeZone.setDefault(tz);
-				
-				String sOldTZ = System.setProperty("persist.sys.timezone", "America/Los_Angeles");
-				
-/*				
-				byte[] buffer = new byte [4096];
-				int	nRead = 0;
-				long lTotalRead = 0;
-
-				Context ctx = SUTAgentAndroid.me.getApplicationContext();
-
-				FTPClient ftp = new FTPClient();
-				try 
-					{
-					String strRet = "";
-					int	reply = 0;
-					FileOutputStream outStream = null;
-					
-					ftp.connect("ftp.mozilla.org");
-					strRet = ftp.getReplyString();
-					reply = ftp.getReplyCode();
-					
-				    if(!FTPReply.isPositiveCompletion(reply))
-				    	{
-				        ftp.disconnect();
-				        System.err.println("FTP server refused connection.");
-				        System.exit(1);
-				        }
-				    // transfer files
-				    
-				    ftp.login("anonymous", "b@t.com");
-					strRet = ftp.getReplyString();
-					reply = ftp.getReplyCode();
-					
-				    if(!FTPReply.isPositiveCompletion(reply))
-				    	{
-				        ftp.disconnect();
-				        System.err.println("FTP server refused connection.");
-				        System.exit(1);
-				        }
-				    
-				    ftp.enterLocalPassiveMode();
-				    
-				    if (ftp.setFileType(FTP.BINARY_FILE_TYPE))
-				    	{
-				    	File root = Environment.getExternalStorageDirectory();
-				    	if (root.canWrite())
-				    		{
-				    		File outFile = new File(root, "firefox-3.6b4.cab");
-				    		outStream = new FileOutputStream(outFile);
-				    		}
-				    	else
-				    		outStream = ctx.openFileOutput("firefox-3.6b4.cab", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
-//				    	outStream = new FileOutputStream("/sdcard/firefox-3.6b4.cab");
-				    	InputStream ftpIn = ftp.retrieveFileStream("pub/mozilla.org/firefox/releases/3.6b4/wince-arm/en-US/firefox-3.6b4.cab");
-						while ((nRead = ftpIn.read(buffer)) != -1)
-							{
-							lTotalRead += nRead;
-							outStream.write(buffer, 0, nRead);
-							strRet = "\r" + lTotalRead + " bytes received";
-							cmdOut.write(strRet.getBytes());
-							cmdOut.flush();
-							}
-						
-						ftpIn.close();
-						boolean bRet = ftp.completePendingCommand();
-						outStream.flush();
-
-				    	/*				    	
-				    	if (ftp.retrieveFile("pub/mozilla.org/firefox/releases/3.6b4/wince-arm/en-US/firefox-3.6b4.cab", outStream))
-				    		{
-				    		outStream.flush();
-				    		}
-				    	 * /				    		
-			    		outStream.close();
-						strRet = ftp.getReplyString();
-						reply = ftp.getReplyCode();
-				    	}
-					strRet = ftp.getReplyString();
-					reply = ftp.getReplyCode();
-				    ftp.logout();
-				    
-				    strReturn = "\r\n" + strRet; 
-					}
-				catch (SocketException e)
-					{
-					// TODO Auto-generated catch block
-					strReturn = e.getMessage();
-					e.printStackTrace();
-					}
-				catch (IOException e)
-					{
-					// TODO Auto-generated catch block
-					strReturn = e.getMessage();
-					e.printStackTrace();
-					}
-*/				
-//				strReturn = InstallApplication();
-//				strReturn = InstallApp(Argv[1], cmdOut);
-				
-//				strReturn = UninstallApplication();
-//				String sPingCheck = SendPing("www.mozilla.org",null);
-//				if (sPingCheck.contains("3 received"))
-//					strReturn = sPingCheck;
-//				RunReboot(cmdOut);
-/*
-				try 
-					{
-					FileOutputStream outFile = ctx.openFileOutput("test.txt", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
-					OutputStreamWriter outS = new OutputStreamWriter(outFile);
-					outS.write("Hello world 1" + lineSep);
-					outS.write("Hello world 2" + lineSep);
-					outS.write("Hello world 3" + lineSep);
-					outS.write("Hello world 4" + lineSep);
-					outS.flush();
-					outS.close();
-					
-					String [] files = ctx.fileList();
-					File aFile   = ctx.getFilesDir();
-					String aPath = aFile.getCanonicalPath();
-					String hold = aFile.getName();
-					
-					strReturn = PrintDir(aPath);
-					strReturn += "\r\n";
-					
-					String src = aPath + "/test.txt";
-					String dst = aPath + "/test2.txt";
-					strReturn += CopyFile(src, dst);
-					strReturn += "\r\n";
-					
-					strReturn += PrintDir(aPath);
-					strReturn += "\r\n";
-					
-					dst = aPath + "/test3.txt";
-					strReturn += Move(src, dst);
-					strReturn += "\r\n";
-					
-					strReturn += PrintDir(aPath);
-					strReturn += "\r\n";
-
-					src = aPath + "/test2.txt";
-					strReturn += RemoveFile(src);
-					strReturn += "\r\n";
-					strReturn += RemoveFile(dst);
-					strReturn += "\r\n";
-					strReturn += PrintDir(aPath);
-					}
-				catch (FileNotFoundException e)
-					{
-					// TODO Auto-generated catch block
-					e.printStackTrace();
-					} 
-				catch (IOException e) 
-					{
-					// TODO Auto-generated catch block
-					e.printStackTrace();
-					}
-*/
-				break;
-				
-			case ENVRUN:
-				if (Argc >= 2)
-					{
-					String [] theArgs = new String [Argc - 1];
-			
-					for (int lcv = 1; lcv < Argc; lcv++)
-						{
-						theArgs[lcv - 1] = Argv[lcv];
-						}
-			
-					strReturn = StartPrg2(theArgs, cmdOut);
-					}
-				else
-					{
-					strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!";
-					}
-				break;
-				
-			case EXEC:
-			case RUN:
-				if (Argc >= 2)
-					{
-					String [] theArgs = new String [Argc - 1];
-				
-					for (int lcv = 1; lcv < Argc; lcv++)
-						{
-						theArgs[lcv - 1] = Argv[lcv];
-						}
-				
-					if (Argv[1].contains("/") || Argv[1].contains("\\") || !Argv[1].contains("."))
-						strReturn = StartPrg(theArgs, cmdOut);
-					else
-						strReturn = StartJavaPrg(theArgs);
-					}
-				else
-					{
-					strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!";
-					}
-				break;
-
-			case KILL:
-				if (Argc == 2)
-//					strReturn = NewKillProc(Argv[1], Argv[2], cmdOut);
-//					strReturn = NewKillProc(Argv[1], cmdOut);
-					strReturn = KillProcess(Argv[1], cmdOut);
-				else
-					strReturn = sErrorPrefix + "Wrong number of arguments for kill command!";
-				break;
-				
-			case DISK:
-				strReturn = GetDiskInfo((Argc == 2 ? Argv[1] : "/"));
-				break;
-				
-			case UNZP:
-				strReturn = Unzip(Argv[1], (Argc == 3 ? Argv[2] : ""));
-				break;
-				
-			case ZIP:
-				strReturn = Zip(Argv[1], (Argc == 3 ? Argv[2] : ""));
-				break;
-				
-			case HELP:
-				strReturn = PrintUsage();
-				break;
-				
-			default:
-				strReturn = sErrorPrefix + "[" + Argv[0] + "] command";
-				if (Argc > 1)
-					{
-					strReturn += " with arg(s) =";
-					for (int lcv = 1; lcv < Argc; lcv++)
-						{
-						strReturn += " [" + Argv[lcv] + "]";
-						}
-					}
-				strReturn += " is currently not implemented.";
-				break;
-			}
-		
-		return(strReturn);
-		}
-/*
-	class AlertLooperThread extends Thread
-		{
-	    public Handler mHandler;
-	    private Looper looper = null;
-	    private DoAlert da	= null;
-	    
-	    public void term()
-	    	{
-	    	if (da != null)
-	    		da.term();
-	    	}
-	    
-	    public void quit()
-	    	{
-	    	if (looper != null)
-	    		looper.quit();
-	    	}
-	    
-	    public void run()
-	    	{
-	        Looper.prepare();
-	        
-	        looper = Looper.myLooper();
-	          
-	        mHandler = new Handler()
-	        	{
-	            public void handleMessage(Message msg)
-	            	{
-	                  // process incoming messages here
-	            	}
-	        	};
-	          
-			alertTimer = new Timer();
-			da = new DoAlert();
-    		alertTimer.scheduleAtFixedRate(da, 0, 5000);
-    		Looper.loop();
-	    	}
-		}
-	
-	class DoAlert extends TimerTask
-		{
-		int	lcv = 0;
-		Toast toast = null;
-		Ringtone rt = null;
-		
-		DoAlert()
-			{
-			Context	ctx = SUTAgentAndroid.me.getApplication().getApplicationContext();
-			this.toast = Toast.makeText(ctx, "Help me!", Toast.LENGTH_LONG);
-			rt = RingtoneManager.getRingtone(ctx, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM));
-			}
-		
-		public void term()
-			{
-			if (rt != null)
-				{
-				if (rt.isPlaying())
-					rt.stop();
-				}
-			
-			if (toast != null)
-				toast.cancel();
-			}
-	
-		public void run ()
-			{
-			String sText =(((lcv++ % 2) == 0)  ? "Help me!" : "I've fallen down!" );
-			toast.setText(sText);
-			toast.show();
-			if (rt != null)
-				rt.play();
-			}
-		}
-*/
-	public void StartAlert()
-		{
-		// start the alert message
-		alrt = new AlertLooperThread(this.contextWrapper);
-		alrt.start();
-		}
-
-	public void StopAlert()
-		{
-		if (alrt == null)
-			return;
-		
-		Timer alertTimer = alrt.getAlertTimer();
-		// stop the alert message
-		if (alertTimer != null)
-			{
-			// Kill the timer
-			alertTimer.cancel();
-			alertTimer.purge();
-			alertTimer = null;
-			// Clear any messages either queued or on screen
-			alrt.term();
-			// Give the messages a chance to be processed
-			try {
-				Thread.sleep(1000);
-				}
-			catch (InterruptedException e)
-				{
-				e.printStackTrace();
-				}
-			// Kill the thread
-			alrt.quit();
-			alrt = null;
-			System.gc();
-			}
-		}
-
-	public String [] parseCmdLine2(String theCmdLine)
-		{
-		String	cmdString;
-		String	workingString;
-		String	workingString2;
-		String	workingString3;
-		List<String> lst = new ArrayList<String>();
-		int nLength = 0;
-		int nFirstSpace = -1;
-		
-		// Null cmd line
-		if (theCmdLine == null)
-			{
-			String [] theArgs = new String [1];
-			theArgs[0] = new String("");
-			return(theArgs);
-			}
-		else
-			{
-			nLength = theCmdLine.length();
-			nFirstSpace = theCmdLine.indexOf(' ');
-			}
-		
-		if (nFirstSpace == -1)
-			{
-			String [] theArgs = new String [1];
-			theArgs[0] = new String(theCmdLine);
-			return(theArgs);
-			}
-		
-		// Get the command
-		cmdString = new String(theCmdLine.substring(0, nFirstSpace)); 
-		lst.add(cmdString);
-		
-		// Jump past the command and trim
-		workingString = (theCmdLine.substring(nFirstSpace + 1, nLength)).trim();
-		
-		while ((nLength = workingString.length()) > 0)
-			{
-			int nEnd = 0;
-			int	nStart = 0;
-			
-			// if we have a quote
-			if (workingString.startsWith("\""))
-				{
-				// point to the first non quote char
-				nStart = 1;
-				// find the matching quote
-				nEnd = workingString.indexOf('"', nStart);
-				
-				char prevChar;
-				
-				while(nEnd != -1)
-					{
-					// check to see if the quotation mark has been escaped
-					prevChar = workingString.charAt(nEnd - 1);
-					if (prevChar == '\\')
-						{
-						// if escaped, point past this quotation mark and find the next
-						nEnd++;
-						if (nEnd < nLength)
-							nEnd = workingString.indexOf('"', nEnd);
-						else
-							nEnd = -1;
-						}
-					else
-						break;
-					}
-				
-				// there isn't one
-				if (nEnd == -1)
-					{
-					// point at the quote
-					nStart = 0;
-					// so find the next space
-					nEnd = workingString.indexOf(' ', nStart);
-					// there isn't one of those either
-					if (nEnd == -1)
-						nEnd = nLength;	// Just grab the rest of the cmdline
-					}
-				}
-			else // no quote so find the next space
-				{
-				nEnd = workingString.indexOf(' ', nStart);
-				// there isn't one of those
-				if (nEnd == -1)
-					nEnd = nLength;	// Just grab the rest of the cmdline
-				}
-			
-			// get the substring
-			workingString2 = workingString.substring(nStart, nEnd);
-
-			// if we have escaped quotes
-			if (workingString2.contains("\\\""))
-				{
-				do
-					{
-					// replace escaped quote with embedded quote
-					workingString3 = workingString2.replace("\\\"", "\"");
-					workingString2 = workingString3;
-					}
-				while(workingString2.contains("\\\""));
-				}
-
-			// add it to the list
-			lst.add(new String(workingString2));
-			
-			// if we are dealing with a quote
-			if (nStart > 0)
-				nEnd++; //  point past the end one
-			
-			// jump past the substring and trim it
-			workingString = (workingString.substring(nEnd)).trim();
-			}
-		
-		// ok we're done package up the results
-		int nItems = lst.size();
-		
-		String [] theArgs = new String [nItems];
-		
-		for (int lcv = 0; lcv < nItems; lcv++)
-			{
-			theArgs[lcv] = lst.get(lcv);
-			}
-	
-		return(theArgs);
-		}
-	
-	public String [] parseCmdLine(String theCmdLine) {
-		String	cmdString;
-		String	workingString;
-		String	workingString2;
-		List<String> lst = new ArrayList<String>();
-		int nLength = 0;
-		int nFirstSpace = -1;
-		
-		// Null cmd line
-		if (theCmdLine == null)
-			{
-			String [] theArgs = new String [1];
-			theArgs[0] = new String("");
-			return(theArgs);
-			}
-		else
-			{
-			nLength = theCmdLine.length();
-			nFirstSpace = theCmdLine.indexOf(' ');
-			}
-		
-		if (nFirstSpace == -1)
-			{
-			String [] theArgs = new String [1];
-			theArgs[0] = new String(theCmdLine);
-			return(theArgs);
-			}
-		
-		// Get the command
-		cmdString = new String(theCmdLine.substring(0, nFirstSpace)); 
-		lst.add(cmdString);
-		
-		// Jump past the command and trim
-		workingString = (theCmdLine.substring(nFirstSpace + 1, nLength)).trim();
-		
-		while ((nLength = workingString.length()) > 0)
-			{
-			int nEnd = 0;
-			int	nStart = 0;
-			
-			// if we have a quote
-			if (workingString.startsWith("\""))
-				{
-				// point to the first non quote char
-				nStart = 1;
-				// find the matching quote
-				nEnd = workingString.indexOf('"', nStart);
-				// there isn't one
-				if (nEnd == -1)
-					{
-					// point at the quote
-					nStart = 0;
-					// so find the next space
-					nEnd = workingString.indexOf(' ', nStart);
-					// there isn't one of those either
-					if (nEnd == -1)
-						nEnd = nLength;	// Just grab the rest of the cmdline
-					}
-				else
-					{
-					nStart = 0;
-					nEnd++;
-					}
-				}
-			else // no quote so find the next space
-				{
-				nEnd = workingString.indexOf(' ', nStart);
-				// there isn't one of those
-				if (nEnd == -1)
-					nEnd = nLength;	// Just grab the rest of the cmdline
-				}
-			
-			// get the substring
-			workingString2 = workingString.substring(nStart, nEnd);
-			
-			// add it to the list
-			lst.add(new String(workingString2));
-			
-			// if we are dealing with a quote
-//			if (nStart > 0)
-//				nEnd++; //  point past the end one
-			
-			// jump past the substring and trim it
-			workingString = (workingString.substring(nEnd)).trim();
-			}
-		
-		int nItems = lst.size();
-		
-		String [] theArgs = new String [nItems];
-		
-		for (int lcv = 0; lcv < nItems; lcv++)
-			{
-			theArgs[lcv] = lst.get(lcv);
-			}
-	
-		return(theArgs);
-		}
-	
-	public String fixFileName(String fileName)
-		{
-		String	sRet = "";
-		String	sTmpFileName = "";
-		
-		sRet = fileName.replace('\\', '/');
-		
-		if (sRet.startsWith("/"))
-			sTmpFileName = sRet;
-		else
-			sTmpFileName = currentDir + "/" + sRet;
-		
-		sRet = sTmpFileName.replace('\\', '/');
-		sTmpFileName = sRet;
-		sRet = sTmpFileName.replace("//", "/");
-		
-		return(sRet);
-		}
-	
-	public String AddFilesToZip(ZipOutputStream out, String baseDir, String relDir)
-	{
-		final int 			BUFFER 	= 2048;
-		String				sRet	= "";
-		String 				curDir 	= "";
-		String				relFN	= "";
-		BufferedInputStream origin = null;
-	    byte 				data[] = new byte[BUFFER];
-	    
-	    if (relDir.length() > 0)
-	    	curDir = baseDir + "/" + relDir;
-	    else
-	    	curDir = baseDir;
-	    
-		File f = new File(curDir);
-		
-		if (f.isFile())
-			{
-		    try {
-		    	relFN = ((relDir.length() > 0) ? relDir + "/" + f.getName() : f.getName());
-		    	System.out.println("Adding: "+relFN);
-		    	sRet += "Adding: "+	relFN + lineSep;
-		    	FileInputStream fi = new FileInputStream(curDir);
-		    	origin = new BufferedInputStream(fi, BUFFER);
-		    	ZipEntry entry = new ZipEntry(relFN);
-		    	out.putNextEntry(entry);
-		    	int count;
-		    	while((count = origin.read(data, 0, BUFFER)) != -1)
-        			{
-		    		out.write(data, 0, count);
-        			}
-		    	origin.close();
-				}
-		    catch(Exception e)
-		    	{
-			    e.printStackTrace();
-		    	}
-		    
-		    return(sRet);
-			}
-		
-		String	files[] = f.list();
-		
-		if (files != null)
-			{
-			try {
-				for(int i = 0; i < files.length; i++)
-					{
-					f = new File(curDir + "/" + files[i]);
-					if (f.isDirectory())
-						{
-						if (relDir.length() > 0)
-							sRet += AddFilesToZip(out, baseDir, relDir + "/" + files[i]);
-						else
-							sRet += AddFilesToZip(out, baseDir, files[i]);
-						}
-					else
-						{
-						relFN = ((relDir.length() > 0) ? relDir + "/" + files[i] : files[i]);
-						System.out.println("Adding: "+relFN);
-						sRet += "Adding: "+	relFN + lineSep;
-						FileInputStream fi = new FileInputStream(curDir + "/" + files[i]);
-						origin = new BufferedInputStream(fi, BUFFER);
-						ZipEntry entry = new ZipEntry(relFN);
-						out.putNextEntry(entry);
-						int count;
-						while((count = origin.read(data, 0, BUFFER)) != -1)
-		        			{
-							out.write(data, 0, count);
-		        			}
-						origin.close();
-						}
-					}
-	    		}
-			catch(Exception e)
-	    		{
-				e.printStackTrace();
-	    		}
-			}
-
-		return(sRet);
-	}
-	
-	public String Zip(String zipFileName, String srcName)
-		{
-		String	fixedZipFileName = fixFileName(zipFileName);
-		String	fixedSrcName = fixFileName(srcName);
-		String sRet = "";
-		
-		try {
-		    FileOutputStream dest = new FileOutputStream(fixedZipFileName);
-		    CheckedOutputStream checksum = new CheckedOutputStream(dest, new Adler32());
-		    ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(checksum));
-		    out.setMethod(ZipOutputStream.DEFLATED);
-		    
-		    sRet += AddFilesToZip(out, fixedSrcName, "");
-		    
-		    out.close();
-		    System.out.println("checksum:		           "+checksum.getChecksum().getValue());
-	        sRet += "checksum:		           "+checksum.getChecksum().getValue();
-		    }
-		catch(Exception e)
-			{
-		    e.printStackTrace();
-		    }
-		
-		return(sRet);
-	}
-
-	public String Unzip(String zipFileName, String dstDirectory)
-		{
-		String 	sRet = "";
-		String	fixedZipFileName = fixFileName(zipFileName);
-		String	fixedDstDirectory = fixFileName(dstDirectory);
-		String	dstFileName = "";
-		int		nNumExtracted = 0;
-		boolean bRet = false;
-		
-		try {
-			final int BUFFER = 2048;
-			BufferedOutputStream dest = null;
-			ZipFile zipFile = new ZipFile(fixedZipFileName);
-			int nNumEntries = zipFile.size();
-			zipFile.close();
-			
-			FileInputStream fis = new FileInputStream(fixedZipFileName);
-			CheckedInputStream checksum = new CheckedInputStream(fis, new Adler32());
-			ZipInputStream zis = new ZipInputStream(new BufferedInputStream(checksum));
-			ZipEntry entry;
-
-			byte [] data = new byte[BUFFER];
-			
-			while((entry = zis.getNextEntry()) != null)
-        		{
-				System.out.println("Extracting: " + entry);
-				int count;
-				if (fixedDstDirectory.length() > 0)
-					dstFileName = fixedDstDirectory + entry.getName();
-				else
-					dstFileName = entry.getName();
-				
-				String tmpDir = dstFileName.substring(0, dstFileName.lastIndexOf('/'));
-				File tmpFile = new File(tmpDir);
-				if (!tmpFile.exists())
-					{
-					bRet = tmpFile.mkdirs();
-					}
-				else
-					bRet = true;
-				
-				if (bRet)
-					{
-					// if we aren't just creating a directory
-					if (dstFileName.lastIndexOf('/') != (dstFileName.length() - 1))
-						{
-						// write out the file
-						FileOutputStream fos = new FileOutputStream(dstFileName);
-						dest = new BufferedOutputStream(fos, BUFFER);
-						while ((count = zis.read(data, 0, BUFFER)) != -1)
-        					{
-							dest.write(data, 0, count);
-        					}
-						dest.flush();
-						dest.close();
-						dest = null;
-						fos.close();
-						fos = null;
-						}
-					nNumExtracted++;
-					}
-				else
-					sRet += " - failed" + lineSep;
-        		}
-			
-			data = null;
-			zis.close();
-			System.out.println("Checksum:          "+checksum.getChecksum().getValue());
-			sRet += "Checksum:          "+checksum.getChecksum().getValue();
-			sRet += lineSep + nNumExtracted + " of " + nNumEntries + " sucessfully extracted";
-			}
-		catch(Exception e)
-			{
-			e.printStackTrace();
-			}
-		
-		return(sRet);
-		}
-	
-	public String StatProcess(String string)
-		{
-		String sRet = "";
-		ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
-		int	[] nPids = new int [1];
-		
-		nPids[0] = Integer.parseInt(string);
-		
-		android.os.Debug.MemoryInfo[] mi = aMgr.getProcessMemoryInfo(nPids);
-		
-		sRet  = "Dalvik Private Dirty pages         " + mi[0].dalvikPrivateDirty     + " kb\n";
-		sRet += "Dalvik Proportional Set Size       " + mi[0].dalvikPss              + " kb\n";
-		sRet += "Dalvik Shared Dirty pages          " + mi[0].dalvikSharedDirty      + " kb\n\n";
-		sRet += "Native Private Dirty pages heap    " + mi[0].nativePrivateDirty     + " kb\n";
-		sRet += "Native Proportional Set Size heap  " + mi[0].nativePss              + " kb\n";
-		sRet += "Native Shared Dirty pages heap     " + mi[0].nativeSharedDirty      + " kb\n\n";
-		sRet += "Other Private Dirty pages          " + mi[0].otherPrivateDirty      + " kb\n";
-		sRet += "Other Proportional Set Size        " + mi[0].otherPss               + " kb\n";
-		sRet += "Other Shared Dirty pages           " + mi[0].otherSharedDirty       + " kb\n\n";
-		sRet += "Total Private Dirty Memory         " + mi[0].getTotalPrivateDirty() + " kb\n";
-		sRet += "Total Proportional Set Size Memory " + mi[0].getTotalPss()          + " kb\n";
-		sRet += "Total Shared Dirty Memory          " + mi[0].getTotalSharedDirty()  + " kb";
-		
-		
-		return(sRet);
-		}
-	
-	public String GetTestRoot()
-		{
-		String	sRet = null;
-		
-		if (Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED))
-			{
-			sRet = Environment.getExternalStorageDirectory().getAbsolutePath();
-			}
-		else
-			{
-			sRet = GetTmpDir();			
-			}
-		
-		return(sRet);
-		}
-	
-	public String GetAppRoot(String AppName)
-		{
-		String sRet = "";
-		Context ctx = contextWrapper.getApplicationContext();
-		
-		if (ctx != null)
-			{
-			try {
-				Context appCtx = ctx.createPackageContext(AppName, 0);
-				ContextWrapper appCtxW = new ContextWrapper(appCtx);
-				sRet = appCtxW.getPackageResourcePath();
-				appCtxW = null;
-				appCtx = null;
-				ctx = null;
-				System.gc();
-				}
-			catch (NameNotFoundException e)
-				{
-				e.printStackTrace();
-				}
-			}
-		return(sRet);
-		}
-
-	public String changeDir(String newDir)
-		{
-		String	tmpDir	= fixFileName(newDir);
-		String	sRet = sErrorPrefix + "Couldn't change directory to " + tmpDir;
-		
-		File tmpFile = new java.io.File(tmpDir);
-		
-		if (tmpFile.exists())
-			{
-			try {
-				currentDir = tmpFile.getCanonicalPath();
-				sRet = "";
-				}
-			catch (IOException e)
-				{
-				// TODO Auto-generated catch block
-				e.printStackTrace();
-				}
-			}
-		
-		return(sRet);
-		}
-	
-	static final String HEXES = "0123456789abcdef";
-	
-	public static String getHex( byte [] raw )
-		{
-	    if ( raw == null )
-	    	{
-	    	return null;
-	    	}
-	    
-	    final StringBuilder hex = new StringBuilder( 2 * raw.length );
-	    for ( final byte b : raw )
-	    	{
-	    	hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
-	    	}
-	    return hex.toString();
-		}
-
-	public String HashFile(String fileName)
-		{
-		String			sTmpFileName = fixFileName(fileName);
-		String			sRet 		= sErrorPrefix + "Couldn't calculate hash for file " + sTmpFileName;
-		byte[] 			buffer 		= new byte [4096];
-		int				nRead 		= 0;
-		long 			lTotalRead 	= 0;
-		MessageDigest	digest 		= null;
-		
-		try {
-			digest = java.security.MessageDigest.getInstance("MD5");
-			}
-		catch (NoSuchAlgorithmException e)
-			{
-			e.printStackTrace();
-			}
-		
-		try {
-			FileInputStream srcFile  = new FileInputStream(sTmpFileName);
-			while((nRead = srcFile.read(buffer)) != -1)
-				{
-				digest.update(buffer, 0, nRead);
-				lTotalRead += nRead;
-				}
-			srcFile.close();
-			byte [] hash = digest.digest();
-			
-			sRet = getHex(hash);
-			}
-		catch (FileNotFoundException e)
-			{
-			sRet += " file not found";
-			e.printStackTrace();
-			}
-		catch (IOException e)
-			{
-			sRet += " io exception";
-			e.printStackTrace();
-			} 
-		return(sRet);
-		}
-	
-	public String RemoveFile(String fileName)
-		{
-		String	sTmpFileName = fixFileName(fileName);
-		String	sRet = sErrorPrefix + "Couldn't delete file " + sTmpFileName;
-		
-		File f = new File(sTmpFileName);
-		
-		if (f.delete())
-			sRet = "deleted " + sTmpFileName;
-		
-		return(sRet);
-		}
-	
-	public String PruneDir(String sDir)
-		{
-		String	sRet = "";
-		int nFiles = 0;
-		String sSubDir = null;
-		String	sTmpDir = fixFileName(sDir);
-		
-		File dir = new File(sTmpDir);
-		
-		if (dir.isDirectory())
-			{
-			sRet = "Deleting file(s) from " + sTmpDir;
-			
-			File [] files = dir.listFiles();
-			if (files != null)
-				{
-				if ((nFiles = files.length) > 0)
-					{
-					for (int lcv = 0; lcv < nFiles; lcv++)
-						{
-						if (files[lcv].isDirectory())
-							{
-							sSubDir = files[lcv].getAbsolutePath();
-							sRet += "\n" + PruneDir(sSubDir);
-							}
-						else
-							{
-							if (files[lcv].delete())
-								{
-								sRet += "\n\tDeleted " + files[lcv].getName();
-								}
-							else
-								{
-								sRet += "\n\tUnable to delete " + files[lcv].getName();
-								}
-							}
-						}
-					}
-				else
-					sRet += "\n\t<empty>";
-				}
-			
-			if (dir.delete())
-				{
-				sRet += "\nDeleting directory " + sTmpDir;
-				}
-			else
-				{
-				sRet += "\nUnable to delete directory " + sTmpDir;
-				}
-			}
-		else
-			{
-			sRet += sErrorPrefix + sTmpDir + " is not a directory";
-			}
-		
-		return(sRet);
-		}
-	
-	public String PrintDir(String sDir)
-		{
-		String	sRet = "";
-		int nFiles = 0;
-		String	sTmpDir = fixFileName(sDir);
-		
-		File dir = new File(sTmpDir);
-		
-		if (dir.isDirectory())
-			{
-			File [] files = dir.listFiles();
-		
-			if (files != null)
-				{
-				if ((nFiles = files.length) > 0)
-					{
-					for (int lcv = 0; lcv < nFiles; lcv++)
-						{
-						sRet += files[lcv].getName();
-						if (lcv < (nFiles - 1))
-							sRet += "\n";
-						}
-					}
-				else
-					sRet = "<empty>";
-				}
-			}
-		else
-			{
-			sRet = sErrorPrefix + sTmpDir + " is not a directory";
-			}
-		
-		return(sRet);
-		}
-	
-	public String Move(String srcFileName, String dstFileName)
-		{
-		String	sTmpSrcFileName = fixFileName(srcFileName);
-		String	sTmpDstFileName = fixFileName(dstFileName);
-		String sRet = sErrorPrefix + "Could not move " + sTmpSrcFileName + " to " + sTmpDstFileName;
-		
-		File srcFile = new File(sTmpSrcFileName);
-		File dstFile = new File(sTmpDstFileName);
-		
-		if (srcFile.renameTo(dstFile))
-			sRet = sTmpSrcFileName + " moved to " + sTmpDstFileName;
-		
-		return (sRet);
-		}
-	
-	public String CopyFile(String srcFileName, String dstFileName)
-		{
-		String	sTmpSrcFileName = fixFileName(srcFileName);
-		String	sTmpDstFileName = fixFileName(dstFileName);
-		String sRet = sErrorPrefix + "Could not copy " + sTmpSrcFileName + " to " + sTmpDstFileName;
-		File destFile = null;
-		byte[] buffer = new byte [4096];
-		int	nRead = 0;
-		long lTotalRead = 0;
-		long lTotalWritten = 0;
-		
-		try 
-			{
-			FileInputStream srcFile  = new FileInputStream(sTmpSrcFileName);
-			FileOutputStream dstFile = new FileOutputStream(sTmpDstFileName);
-			
-			while((nRead = srcFile.read(buffer)) != -1)
-				{
-				lTotalRead += nRead;
-				dstFile.write(buffer, 0, nRead);
-				}
-			dstFile.flush();
-			dstFile.close();
-			
-			destFile = new File(sTmpDstFileName);
-			lTotalWritten = destFile.length();
-
-			if (lTotalWritten == lTotalRead)
-				sRet = sTmpSrcFileName + " copied to " + sTmpDstFileName;
-			else
-				sRet = sErrorPrefix + "Failed to copy " + sTmpSrcFileName + " [length = " + lTotalWritten + "] to " + sTmpDstFileName + " [length = " + lTotalRead + "]";
-			}
-		catch (FileNotFoundException e)
-			{
-			e.printStackTrace();
-			} 
-		catch (IOException e)
-			{
-			e.printStackTrace();
-			}
-
-		return (sRet);
-		}
-	
-	public String IsDirWritable(String sDir)
-		{
-		String sRet = "";
-		String	sTmpDir = fixFileName(sDir);
-		File dir = new File(sTmpDir);
-		
-		if (dir.isDirectory())
-			{
-			sRet = "[" + sDir + "] " + (dir.canWrite() ? "is" : "is not") + " writable";
-			}
-		else
-			{
-			sRet = sErrorPrefix + "[" + sDir + "] is not a directory";
-			}
-		
-		return(sRet);
-		}
-	
-	public String Push(String fileName, BufferedInputStream bufIn, long lSize)
-	{
-		byte []				buffer 			= new byte [8192];
-		int					nRead			= 0;
-		long				lRead			= 0;
-		String				sTmpFileName 	= fixFileName(fileName);
-		String				sRet			= sErrorPrefix + "Push failed!";
-		
-		try {
-			FileOutputStream dstFile = new FileOutputStream(sTmpFileName, false);
-			while((nRead != -1) && (lRead < lSize))
-				{
-				nRead = bufIn.read(buffer);
-				if (nRead != -1)
-					{
-					dstFile.write(buffer, 0, nRead);
-					dstFile.flush();
-					lRead += nRead;
-					}
-				}
-			
-			dstFile.flush();
-			dstFile.close();
-			
-			if (lRead == lSize)
-				{
-				sRet = HashFile(sTmpFileName);
-				}
-			}
-		catch (IOException e)
-			{
-			e.printStackTrace();
-			}
-		
-		buffer = null;
-		
-		return(sRet);
-	}
-	
-	public String FTPGetFile(String sServer, String sSrcFileName, String sDstFileName, OutputStream out)
-		{
-		byte[] buffer = new byte [4096];
-		int	nRead = 0;
-		long lTotalRead = 0;
-		String sRet = sErrorPrefix + "FTP Get failed for " + sSrcFileName;
-		String strRet = "";
-		int	reply = 0;
-		FileOutputStream outStream = null;
-		String	sTmpDstFileName = fixFileName(sDstFileName);
-		
-		FTPClient ftp = new FTPClient();
-		try 
-			{
-			ftp.connect(sServer);
-			reply = ftp.getReplyCode();
-		    if(FTPReply.isPositiveCompletion(reply))
-		    	{
-			    ftp.login("anonymous", "b@t.com");
-				reply = ftp.getReplyCode();
-			    if(FTPReply.isPositiveCompletion(reply))
-			    	{
-				    ftp.enterLocalPassiveMode();
-				    if (ftp.setFileType(FTP.BINARY_FILE_TYPE))
-				    	{
-				    	File dstFile = new File(sTmpDstFileName);
-				    	outStream = new FileOutputStream(dstFile);
-				    	FTPFile [] ftpFiles = ftp.listFiles(sSrcFileName);
-				    	long lFtpSize = ftpFiles[0].getSize();
-				    	if (lFtpSize <= 0)
-				    		lFtpSize = 1;
-				    	
-				    	InputStream ftpIn = ftp.retrieveFileStream(sSrcFileName);
-						while ((nRead = ftpIn.read(buffer)) != -1)
-							{
-							lTotalRead += nRead;
-							outStream.write(buffer, 0, nRead);
-							strRet = "\r" + lTotalRead + " of " + lFtpSize + " bytes received " + ((lTotalRead * 100) / lFtpSize) + "% completed";
-							out.write(strRet.getBytes());
-							out.flush();
-							}
-						ftpIn.close();
-						@SuppressWarnings("unused")
-						boolean bRet = ftp.completePendingCommand();
-						outStream.flush();
-			    		outStream.close();
-						strRet = ftp.getReplyString();
-						reply = ftp.getReplyCode();
-				    	}
-					strRet = ftp.getReplyString();
-					reply = ftp.getReplyCode();
-				    ftp.logout();
-				    ftp.disconnect();
-				    sRet = "\n" + strRet; 
-			    	}
-			    else
-			    	{
-			        ftp.disconnect();
-			        System.err.println("FTP server refused login.");
-			        }
-		    	}
-		    else
-		    	{
-		        ftp.disconnect();
-		        System.err.println("FTP server refused connection.");
-		        }
-			}
-		catch (SocketException e)
-			{
-			sRet = e.getMessage();
-			strRet = ftp.getReplyString();
-			reply = ftp.getReplyCode();
-			sRet += "\n" + strRet;
-			e.printStackTrace();
-			}
-		catch (IOException e)
-			{
-			sRet = e.getMessage();
-			strRet = ftp.getReplyString();
-			reply = ftp.getReplyCode();
-			sRet += "\n" + strRet;
-			e.printStackTrace();
-			}
-		return (sRet);
-	}
-	
-	public String Cat(String fileName, OutputStream out)
-		{
-		String	sTmpFileName = fixFileName(fileName);
-		String	sRet = sErrorPrefix + "Could not read the file " + sTmpFileName;
-		byte[]	buffer = new byte [4096];
-		int		nRead = 0;
-		
-		try {
-			FileInputStream fin = new FileInputStream(sTmpFileName);
-			while ((nRead = fin.read(buffer)) != -1)
-				{
-				out.write(buffer,0,nRead);
-				}
-			out.flush();
-			sRet = "";
-			}
-		catch (FileNotFoundException e)
-			{
-			sRet = e.toString();
-			} 
-		catch (IOException e) 
-			{
-			sRet = e.toString();
-			}
-		return (sRet);
-		}
-	
-	public String MakeDir(String sDir)
-		{
-		String	sTmpDir = fixFileName(sDir);
-		String sRet = sErrorPrefix + "Could not create the directory " + sTmpDir;
-		File dir = new File(sTmpDir);
-		
-		if (dir.mkdirs())
-			sRet = sDir + " successfully created";
-		
-		return (sRet);
-		}
-	// move this to SUTAgentAndroid.java
-	public String GetScreenInfo()
-		{
-		String sRet = "";
-		DisplayMetrics metrics = new DisplayMetrics();
-		WindowManager wMgr = (WindowManager) contextWrapper.getSystemService(Context.WINDOW_SERVICE);
-		wMgr.getDefaultDisplay().getMetrics(metrics);
-		sRet = "X:" + metrics.widthPixels + " Y:" + metrics.heightPixels;
-		return (sRet);
-		}
-	// move this to SUTAgentAndroid.java
-	public int [] GetScreenXY()
-		{
-			int [] nRetXY = new int [2];
-			DisplayMetrics metrics = new DisplayMetrics();
-			WindowManager wMgr = (WindowManager) contextWrapper.getSystemService(Context.WINDOW_SERVICE);
-			wMgr.getDefaultDisplay().getMetrics(metrics);
-			nRetXY[0] = metrics.widthPixels;
-			nRetXY[1] = metrics.heightPixels;
-			return(nRetXY);
-		}
-	
-	public String KillProcess(String sProcName, OutputStream out)
-		{
-		String [] theArgs = new String [3];
-		
-		theArgs[0] = "su";
-		theArgs[1] = "-c";
-		theArgs[2] = "kill";
-
-		String sRet = sErrorPrefix + "Unable to kill " + sProcName + "\n";
-		ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
-		List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses();
-		int lcv = 0;
-		String strProcName = "";
-		int	nPID = 0;
-		
-		for (lcv = 0; lcv < lProcesses.size(); lcv++)
-			{
-			if (lProcesses.get(lcv).processName.contains(sProcName))
-				{
-				strProcName = lProcesses.get(lcv).processName;
-				nPID = lProcesses.get(lcv).pid;
-				sRet = sErrorPrefix + "Failed to kill " + nPID + " " + strProcName + "\n";
-
-				theArgs[2] += " " + nPID;
-				
-				try 
-					{
-					pProc = Runtime.getRuntime().exec(theArgs);
-					RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
-					outThrd.start();
-					outThrd.join(5000);
-					}
-				catch (IOException e) 
-					{
-					sRet = e.getMessage();
-					e.printStackTrace();
-					} 
-				catch (InterruptedException e)
-					{
-					e.printStackTrace();
-					}
-
-				// Give the messages a chance to be processed
-				try {
-					Thread.sleep(2000);
-					}
-				catch (InterruptedException e)
-					{
-					e.printStackTrace();
-					}
-				break;
-				}
-			}
-		
-		if (nPID > 0)
-			{
-			sRet = "Successfully killed " + nPID + " " + strProcName + "\n";
-			lProcesses = aMgr.getRunningAppProcesses();
-			for (lcv = 0; lcv < lProcesses.size(); lcv++)
-				{
-				if (lProcesses.get(lcv).processName.contains(sProcName))
-					{
-					sRet = sErrorPrefix + "Unable to kill " + nPID + " " + strProcName + "\n";
-					break;
-					}
-				}
-			}
-		
-		return (sRet);
-		}
-
-	public boolean IsProcessDead(String sProcName)
-		{
-		boolean bRet = false;
-		ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
-		List <ActivityManager.ProcessErrorStateInfo> lProcesses = aMgr.getProcessesInErrorState();
-		int lcv = 0;
-		
-		if (lProcesses != null)
-			{
-			for (lcv = 0; lcv < lProcesses.size(); lcv++)
-				{
-				if (lProcesses.get(lcv).processName.contentEquals(sProcName) && 
-					lProcesses.get(lcv).condition != ActivityManager.ProcessErrorStateInfo.NO_ERROR)
-					{
-					bRet = true;
-					break;
-					}
-				}
-			}
-	
-		return (bRet);
-		}
-
-	public String GetProcessInfo()
-		{
-		String sRet = "";
-		ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
-		List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses();
-		int	nProcs = lProcesses.size();
-		int lcv = 0;
-		String strProcName = "";
-		int	nPID = 0;
-		int nUser = 0;
-		
-		for (lcv = 0; lcv < nProcs; lcv++)
-			{
-			strProcName = lProcesses.get(lcv).processName;
-			nPID = lProcesses.get(lcv).pid;
-			nUser = lProcesses.get(lcv).uid;
-			sRet += nUser + "\t" + nPID + "\t" + strProcName;
-			if (lcv < (nProcs - 1))
-				sRet += "\n";
-			}
-			
-		return (sRet);
-		}
-	
-	public String GetOSInfo()
-		{
-		String sRet = "";
-		
-		sRet = Build.DISPLAY;
-		
-		return (sRet);
-		}
-	
-	public String GetPowerInfo()
-		{
-		String sRet = "";
-	
-		sRet = "Power status:\n  AC power " + SUTAgentAndroid.sACStatus + "\n";
-		sRet += "  Battery charge " + SUTAgentAndroid.sPowerStatus + "\n";
-		sRet += "  Remaining charge:      " + SUTAgentAndroid.nChargeLevel + "%\n";
-		sRet += "  Battery Temperature:   " + (((float)(SUTAgentAndroid.nBatteryTemp))/10) + " (c)\n";
-		return (sRet);
-		}
-
-	public String GetDiskInfo(String sPath)
-		{
-		String sRet = "";
-		StatFs statFS = new StatFs(sPath);
-		
-		int nBlockCount = statFS.getBlockCount();
-		int nBlockSize = statFS.getBlockSize();
-		int nBlocksAvail = statFS.getAvailableBlocks();
-		int nBlocksFree = statFS.getFreeBlocks();
-	
-		sRet = "total:     " + (nBlockCount * nBlockSize) + "\nfree:      " + (nBlocksFree * nBlockSize) + "\navailable: " + (nBlocksAvail * nBlockSize);
-	
-		return (sRet);
-		}
-
-	public String GetMemoryInfo()
-		{
-		String sRet = "PA:" + GetMemoryConfig();
-		return (sRet);
-		}
-
-	public long GetMemoryConfig()
-		{
-		ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
-		ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
-		aMgr.getMemoryInfo(outInfo);
-		long lMem = outInfo.availMem;
-
-		return (lMem);
-		}
-	
-	public String RegisterTheDevice(String sSrvr, String sPort, String sData)
-		{
-		String sRet = "";
-		String line = "";
-		
-		if (sSrvr != null && sPort != null && sData != null)
-			{
-			try
-				{
-				int nPort = Integer.parseInt(sPort);
-				Socket socket = new Socket(sSrvr, nPort);
-				PrintWriter out = new PrintWriter(socket.getOutputStream(), false);
-				BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
-				out.println(sData);
-				if ( out.checkError() == false )
-					{
-					socket.setSoTimeout(30000);
-					while (socket.isInputShutdown() == false)
-						{
-						line = in.readLine();
-						line = line.toLowerCase();
-						if ((line == null) || (line.contains("ok")))
-							{
-							sRet += line;
-							break;
-							}
-						sRet += line;
-						}
-					}
-				out.close();
-				in.close();
-				socket.close();
-				}
-			catch(NumberFormatException e)
-				{
-				e.printStackTrace();
-				} 
-			catch (UnknownHostException e)
-				{
-				e.printStackTrace();
-				}
-			catch (IOException e)
-				{
-				sRet += "reg exception thrown";
-				e.printStackTrace();
-				}
-			}
-		return(sRet);
-		}
-	
-	public String GetInternetData(String sHost, String sPort, String sURL)
-		{
-		String sRet = "";
-		String sNewURL = "";
-		HttpClient httpClient = new DefaultHttpClient();
-		try 
-			{
-			sNewURL = "http://" + sHost + ((sPort.length() > 0) ? (":" + sPort) : "") + sURL;
-
-			HttpGet request = new HttpGet(sNewURL);
-			HttpResponse response = httpClient.execute(request);
-			int status = response.getStatusLine().getStatusCode();
-			// we assume that the response body contains the error message
-			if (status != HttpStatus.SC_OK)
-				{
-			    ByteArrayOutputStream ostream = new ByteArrayOutputStream();
-			    response.getEntity().writeTo(ostream);
-			    Log.e("HTTP CLIENT", ostream.toString());
-				}
-			else
-				{
-			    InputStream content = response.getEntity().getContent();
-//			    int nAvailable = content.available();
-//			    byte [] data = new byte [nAvailable];
-			    byte [] data = new byte [2048];
-			    int nRead = content.read(data);
-			    sRet = new String(data, 0, nRead);
-			    content.close(); // this will also close the connection
-				}
-			}
-		catch (IllegalArgumentException e)
-			{
-			sRet = e.getLocalizedMessage();
-			e.printStackTrace();
-			}
-		catch (ClientProtocolException e)
-			{
-			sRet = e.getLocalizedMessage();
-			e.printStackTrace();
-			}
-		catch (IOException e)
-			{
-			sRet = e.getLocalizedMessage();
-			e.printStackTrace();
-			}
-		
-		return(sRet);
-		}
-	
-	public String GetTimeZone()
-		{
-		String	sRet = "";
-		TimeZone tz;
-		
-		tz = TimeZone.getDefault();
-		Date now = new Date();
-		sRet = tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG);
-		
-		return(sRet);
-		}
-	
-	public String SetTimeZone(String sTimeZone)
-		{
-		String			sRet = "Unable to set timezone to " + sTimeZone;
-		TimeZone 		tz = null;
-		AlarmManager 	amgr = null;
-		
-		if ((sTimeZone.length() > 0) && (sTimeZone.startsWith("GMT")))
-			{
-			amgr = (AlarmManager) contextWrapper.getSystemService(Context.ALARM_SERVICE);
-			if (amgr != null)
-				amgr.setTimeZone(sTimeZone);
-			}
-		else
-			{
-			String [] zoneNames = TimeZone.getAvailableIDs();
-			int nNumMatches = zoneNames.length;
-			int	lcv = 0;
-			
-			for (lcv = 0; lcv < nNumMatches; lcv++)
-				{
-				if (zoneNames[lcv].equalsIgnoreCase(sTimeZone))
-					break;
-				}
-
-			if (lcv < nNumMatches)
-				{
-				amgr = (AlarmManager) contextWrapper.getSystemService(Context.ALARM_SERVICE);
-				if (amgr != null)
-					amgr.setTimeZone(zoneNames[lcv]);
-				}
-			}
-		
-		if (amgr != null)
-			{
-			tz = TimeZone.getDefault();
-			Date now = new Date();
-			sRet = tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG);
-			}
-		
-		return(sRet);
-		}
-
-	public String GetSystemTime()
-		{
-		String sRet = "";
-		Calendar cal = Calendar.getInstance();
-		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS");
-		sRet = sdf.format(cal.getTime());
-	
-		return (sRet);
-		}
-	
-	public String SetSystemTime(String sDate, String sTime, OutputStream out)
-		{
-		String sRet = "";
-		
-		if (((sDate != null) && (sTime != null)) && 
-			(sDate.contains("/") || sDate.contains(".")) &&
-			(sTime.contains(":")))
-			{
-			int year = Integer.parseInt(sDate.substring(0,4));
-			int month = Integer.parseInt(sDate.substring(5,7));
-			int day = Integer.parseInt(sDate.substring(8,10));
-			
-			int hour = Integer.parseInt(sTime.substring(0,2));
-			int mins = Integer.parseInt(sTime.substring(3,5));
-			int secs = Integer.parseInt(sTime.substring(6,8));
-
-			Calendar cal = new GregorianCalendar(TimeZone.getDefault());
-			cal.set(year, month - 1, day, hour, mins, secs);
-			long lMillisecs = cal.getTime().getTime();
-			
-			String sM = Long.toString(lMillisecs);
-			String sMillis = sM.substring(0, sM.length() - 3) + "." + sM.substring(sM.length() - 3);
-			String [] theArgs = new String [3];
-		
-			theArgs[0] = "su";
-			theArgs[1] = "-c";
-			theArgs[2] = "date -u " + sMillis;
-		
-			try 
-				{
-				pProc = Runtime.getRuntime().exec(theArgs);
-				RedirOutputThread outThrd = new RedirOutputThread(pProc, null);
-				outThrd.start();
-				outThrd.join(10000);
-				sRet = GetSystemTime();
-				}
-			catch (IOException e) 
-				{
-				sRet = e.getMessage();
-				e.printStackTrace();
-				} 
-			catch (InterruptedException e)
-				{
-				e.printStackTrace();
-				}
-			}
-		else
-			{
-			sRet = "Invalid argument(s)";
-			}
-
-		return (sRet);
-		}
-
-	public String GetClok()
-		{
-		long lMillisecs = System.currentTimeMillis();
-		String sRet = "";
-		
-		if (lMillisecs > 0)
-			sRet = Long.toString(lMillisecs);
-		
-		return(sRet);
-		}
-	
-	public String GetUptime()
-		{
-		String sRet = "";
-		long lHold = 0;
-		long lUptime = SystemClock.elapsedRealtime();
-		int	nDays = 0;
-		int	nHours = 0;
-		int nMinutes = 0;
-		int nSecs = 0;
-		int nMilliseconds = 0;
-		
-		if (lUptime > 0)
-			{
-			nDays = (int)(lUptime / (24L * 60L * 60L * 1000L));
-			lHold = lUptime % (24L * 60L * 60L * 1000L);
-			nHours = (int)(lHold / (60L * 60L * 1000L));
-			lHold %= 60L * 60L * 1000L;
-			nMinutes = (int)(lHold / (60L * 1000L));
-			lHold %= 60L * 1000L;
-			nSecs = (int)(lHold / 1000L);
-			nMilliseconds = (int)(lHold % 1000);
-			sRet = "" + nDays + " days " + nHours + " hours " + nMinutes + " minutes " + nSecs + " seconds " + nMilliseconds + " ms";
-			}
-
-		return (sRet);
-		}
-
-	public String NewKillProc(String sProcId, OutputStream out)
-		{
-		String sRet = "";
-		String [] theArgs = new String [3];
-		
-		theArgs[0] = "su";
-		theArgs[1] = "-c";
-		theArgs[2] = "kill " + sProcId;
-
-		try 
-			{
-			pProc = Runtime.getRuntime().exec(theArgs);
-			RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
-			outThrd.start();
-			outThrd.join(5000);
-			}
-		catch (IOException e) 
-			{
-			sRet = e.getMessage();
-			e.printStackTrace();
-			} 
-		catch (InterruptedException e)
-			{
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-			}
-
-		return(sRet);
-		}
-	
-	public String SendPing(String sIPAddr, OutputStream out)
-		{
-		String sRet = "";
-		String [] theArgs = new String [4];
-		
-		theArgs[0] = "ping";
-		theArgs[1] = "-c";
-		theArgs[2] = "3";
-		theArgs[3] = sIPAddr;
-		
-		try 
-			{
-			pProc = Runtime.getRuntime().exec(theArgs);
-			RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
-			outThrd.start();
-			outThrd.join(5000);
-			if (out == null)
-				sRet = outThrd.strOutput;
-			}
-		catch (IOException e) 
-			{
-			sRet = e.getMessage();
-			e.printStackTrace();
-			} 
-		catch (InterruptedException e)
-			{
-			e.printStackTrace();
-			}
-	
-		return (sRet);
-		}
-	
-	public String GetTmpDir()
-	{
-		String 	sRet = "";
-		Context ctx = contextWrapper.getApplicationContext();
-        File dir = ctx.getFilesDir();
-        ctx = null;
-        try {
-			sRet = dir.getCanonicalPath();
-			} 
-        catch (IOException e)
-        	{
-			e.printStackTrace();
-        	}
-        return(sRet);
-	}
-	
-	public String PrintFileTimestamp(String sFile)
-		{
-		String 	sRet = "";
-		String	sTmpFileName = fixFileName(sFile);
-		File 	theFile = new File(sTmpFileName);
-		
-		if (theFile.exists())
-			{
-			long lModified = theFile.lastModified();
-			Date dtModified = new Date(lModified);
-			SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS");
-			sRet = "Last modified: " + sdf.format(dtModified);
-			}
-		else
-			{
-			sRet = sErrorPrefix + "[" + sTmpFileName + "] doesn't exist";
-			}
-
-		return(sRet);
-		}
-	
-	public String GetIniData(String sSection, String sKey, String sFile)
-		{
-		String sRet = "";
-		String sComp = "";
-		String sLine = "";
-		boolean bFound = false;
-		BufferedReader in = null;
-		String	sTmpFileName = fixFileName(sFile);
-		
-		try {
-			in = new BufferedReader(new FileReader(sTmpFileName));
-			sComp = "[" + sSection + "]";
-			while ((sLine = in.readLine()) != null)
-				{
-				if (sLine.equalsIgnoreCase(sComp))
-					{
-					bFound = true;
-					break;
-					}
-				}
-			
-			if (bFound)
-				{
-				sComp = (sKey + " =").toLowerCase();
-				while ((sLine = in.readLine()) != null)
-					{
-					if (sLine.toLowerCase().contains(sComp))
-						{
-						String [] temp = null;
-						temp = sLine.split("=");
-						if (temp != null)
-							{
-							sRet = temp[1].trim();
-							}
-						break;
-						}
-					}
-				}
-			in.close();
-			}
-		catch (FileNotFoundException e)
-			{
-			sComp = e.toString();
-			} 
-		catch (IOException e) 
-			{
-			sComp = e.toString();
-			}
-		return (sRet);
-		}
-	
-	public String RunReboot(OutputStream out)
-		{
-		String sRet = "";
-		String [] theArgs = new String [3];
-	
-		theArgs[0] = "su";
-		theArgs[1] = "-c";
-		theArgs[2] = "reboot";
-	
-		try 
-			{
-			// Tell all of the data channels we are rebooting
-			((ASMozStub)this.contextWrapper).SendToDataChannel("Rebooting ...");
-			
-			pProc = Runtime.getRuntime().exec(theArgs);
-			RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
-			outThrd.start();
-			outThrd.join(10000);
-			}
-		catch (IOException e) 
-			{
-			sRet = e.getMessage();
-			e.printStackTrace();
-			} 
-		catch (InterruptedException e)
-			{
-			e.printStackTrace();
-			}
-
-		return (sRet);
-		}
-	
-	public String UnInstallApp(String sApp, OutputStream out)
-		{
-		String sRet = "";
-		String [] theArgs = new String [3];
-
-		theArgs[0] = "su";
-		theArgs[1] = "-c";
-		theArgs[2] = "pm uninstall " + sApp + ";reboot;exit";
-		
-		try 
-			{
-			pProc = Runtime.getRuntime().exec(theArgs);
-		
-			RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
-			outThrd.start();
-			outThrd.join(60000);
-			int nRet = pProc.exitValue();
-			sRet = "\nuninst complete [" + nRet + "]";
-			}
-		catch (IOException e) 
-			{
-			sRet = e.getMessage();
-			e.printStackTrace();
-			} 
-		catch (InterruptedException e)
-			{
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-			}
-		
-		return (sRet);
-	}
-	
-	public String InstallApp(String sApp, OutputStream out)
-		{
-		String sRet = "";
-		String [] theArgs = new String [3];
-		File	srcFile = new File(sApp);
-//		boolean bDone = false;
-//		int		nExitCode;
-
-		theArgs[0] = "su";
-		theArgs[1] = "-c";
-		theArgs[2] = "mv " + GetTmpDir() + "/" + srcFile.getName() + " /data/local/tmp/" + srcFile.getName() + ";exit";
-//		theArgs[2] += ";chmod 666 /data/local/tmp/" + srcFile.getName();
-//		theArgs[2] += ";pm install /data/local/tmp/" + srcFile.getName() + " Cleanup";
-//		theArgs[2] += ";done;exit";
-		
-		sRet = CopyFile(sApp, GetTmpDir() + "/" + srcFile.getName());
-		try {
-			out.write(sRet.getBytes());
-			out.flush();
-		} catch (IOException e1) {
-			// TODO Auto-generated catch block
-			e1.printStackTrace();
-		}
-//		CopyFile(sApp, GetTmpDir() + "/" + srcFile.getName());
-
-		try 
-			{
-			pProc = Runtime.getRuntime().exec(theArgs);
-			
-			RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
-			outThrd.start();
-			outThrd.join(90000);
-			int nRet = pProc.exitValue();
-//			boolean bRet = outThrd.isAlive();
-			sRet = "\nmove complete [" + nRet + "]";
-			try 
-				{
-				out.write(sRet.getBytes());
-				out.flush();
-				}
-			catch (IOException e1)
-				{
-				// TODO Auto-generated catch block
-				e1.printStackTrace();
-				}
-			
-			theArgs[2] = "chmod 666 /data/local/tmp/" + srcFile.getName() + ";exit";
-			pProc = Runtime.getRuntime().exec(theArgs);
-			RedirOutputThread outThrd2 = new RedirOutputThread(pProc, out);
-			outThrd2.start();
-			outThrd2.join(10000);
-			int nRet2 = pProc.exitValue();
-//			bRet = outThrd2.isAlive();
-			sRet = "\npermission change complete [" + nRet2 + "]\n";
-			try {
-				out.write(sRet.getBytes());
-				out.flush();
-				}
-			catch (IOException e1)
-				{
-				// TODO Auto-generated catch block
-				e1.printStackTrace();
-				}
-			
-			theArgs[2] = "pm install /data/local/tmp/" + srcFile.getName() + " Cleanup" + ";exit";
-			pProc = Runtime.getRuntime().exec(theArgs);
-			RedirOutputThread outThrd3 = new RedirOutputThread(pProc, out);
-			outThrd3.start();
-			outThrd3.join(60000);
-			int nRet3 = pProc.exitValue();
-			sRet = "\ninstallation complete [" + nRet3 + "]";
-			try {
-				out.write(sRet.getBytes());
-				out.flush();
-				}
-			catch (IOException e1)
-				{
-				// TODO Auto-generated catch block
-				e1.printStackTrace();
-				}
-			
-			theArgs[2] = "rm /data/local/tmp/" + srcFile.getName() + ";exit";
-			pProc = Runtime.getRuntime().exec(theArgs);
-			RedirOutputThread outThrd4 = new RedirOutputThread(pProc, out);
-			outThrd4.start();
-			outThrd4.join(60000);
-			int nRet4 = pProc.exitValue();
-			sRet = "\ntmp file removed [" + nRet4 + "]";
-			try {
-				out.write(sRet.getBytes());
-				out.flush();
-				}
-			catch (IOException e1)
-				{
-				// TODO Auto-generated catch block
-				e1.printStackTrace();
-				}
-			sRet = "\nSuccess";
-			}
-		catch (IOException e) 
-			{
-			sRet = e.getMessage();
-			e.printStackTrace();
-			} 
-		catch (InterruptedException e)
-			{
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-			}
-
-		return (sRet);
-		}
-
-	public String StartUpdateOMatic(String sPkgName, String sPkgFileName)
-		{
-		String sRet = "";
-	
-		Context ctx = contextWrapper.getApplicationContext();
-		PackageManager pm = ctx.getPackageManager();
-
-		Intent prgIntent = new Intent();
-		prgIntent.setPackage("com.mozilla.UpdateOMatic");
-		prgIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-		try {
-			PackageInfo pi = pm.getPackageInfo("com.mozilla.UpdateOMatic", PackageManager.GET_ACTIVITIES | PackageManager.GET_INTENT_FILTERS);
-			ActivityInfo [] ai = pi.activities;
-			for (int i = 0; i < ai.length; i++)
-				{
-				ActivityInfo a = ai[i];
-				if (a.name.length() > 0)
-					{
-					prgIntent.setClassName(a.packageName, a.name);
-					break;
-					}
-				}
-			}
-		catch (NameNotFoundException e)
-			{
-			e.printStackTrace();
-			}
-		
-		prgIntent.putExtra("pkgName", sPkgName);
-		prgIntent.putExtra("pkgFileName", sPkgFileName);
-
-		try 
-			{
-			contextWrapper.startActivity(prgIntent);
-			sRet = "exit";
-			}
-		catch(ActivityNotFoundException anf)
-			{
-			anf.printStackTrace();
-			} 
-	
-		ctx = null;
-		return (sRet);
-		}
-
-	public String StartJavaPrg(String [] sArgs)
-		{
-		String sRet = "";
-		String sArgList = "";
-		String sUrl = "";
-		String sRedirFileName = "";
-		
-		Context ctx = contextWrapper.getApplicationContext();
-		PackageManager pm = ctx.getPackageManager();
-
-		Intent prgIntent = new Intent();
-		prgIntent.setPackage(sArgs[0]);
-		prgIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-		try {
-			PackageInfo pi = pm.getPackageInfo(sArgs[0], PackageManager.GET_ACTIVITIES | PackageManager.GET_INTENT_FILTERS);
-			ActivityInfo [] ai = pi.activities;
-			for (int i = 0; i < ai.length; i++)
-				{
-				ActivityInfo a = ai[i];
-				if (a.name.length() > 0)
-					{
-					prgIntent.setClassName(a.packageName, a.name);
-					break;
-					}
-				}
-			}
-		catch (NameNotFoundException e)
-			{
-			e.printStackTrace();
-			}
-		
-		if (sArgs.length > 1)
-			{
-//			if (sArgs[0].contains("android.browser"))
-				prgIntent.setAction(Intent.ACTION_VIEW);
-			
-			if (sArgs[0].contains("fennec"))
-				{
-				sArgList = "";
-				sUrl = "";
-				
-				for (int lcv = 1; lcv < sArgs.length; lcv++)
-					{
-					if (sArgs[lcv].contains("://"))
-						sUrl = sArgs[lcv];
-					else
-						{
-						if (sArgs[lcv].equals(">"))
-							{
-							lcv++;
-							if (lcv < sArgs.length)
-								sRedirFileName = sArgs[lcv++];
-							}
-						else
-							sArgList += " " + sArgs[lcv];
-						}
-					}
-				
-				if (sArgList.length() > 0)
-					prgIntent.putExtra("args", sArgList.trim());
-				
-				if (sUrl.length() > 0)
-					prgIntent.setData(Uri.parse(sUrl.trim()));
-				}
-			else
-				{
-				for (int lcv = 1; lcv < sArgs.length; lcv++)
-					sArgList += " " + sArgs[lcv];
-				
-				prgIntent.setData(Uri.parse(sArgList.trim()));
-				}
-			}
-		else
-			prgIntent.setData(Uri.parse("about:blank"));
-
-		try 
-			{
-			contextWrapper.startActivity(prgIntent);
-			}
-		catch(ActivityNotFoundException anf)
-			{
-			anf.printStackTrace();
-			}
-		
-		ctx = null;
-		return (sRet);
-		}
-
-	public String StartPrg(String [] progArray, OutputStream out)
-		{
-		String sRet = "";
-		
-		try 
-			{
-			pProc = Runtime.getRuntime().exec(progArray);
-			RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
-			outThrd.start();
-			outThrd.join(10000);
-			int nRetCode = pProc.exitValue();
-			sRet = "return code [" + nRetCode + "]";
-			}
-		catch (IOException e) 
-			{
-			e.printStackTrace();
-			}
-		catch (InterruptedException e)
-			{
-			e.printStackTrace();
-			sRet = "Timed out!";
-			}
-
-		return (sRet);
-		}
-/*	
-	@SuppressWarnings("unchecked")
-	public static void set(String key, String value) throws Exception
-		{
-	    Class[] classes = Collections.class.getDeclaredClasses();
-	    Map env = System.getenv();
-	    for(Class cl : classes)
-	    	{
-	        if("java.util.Collections$UnmodifiableMap".equals(cl.getName()))
-	        	{
-	            Field field = cl.getDeclaredField("m");
-	            field.setAccessible(true);
-	            Object obj = field.get(env);
-	            Map<String, String> map = (Map<String, String>) obj;
-	            map.put(key, value);
-	        	}
-	    	}
-		}
-
-*/	
-	public String StartPrg2(String [] progArray, OutputStream out)
-		{
-		String sRet = "";
-		
-		int	nArraySize = 0;
-		int	nArgs = progArray.length - 1; // 1st arg is the environment string
-		int	lcv	= 0;
-		int	temp = 0;
-
-		String sEnvString = progArray[0];
-
-		// Set up command line args stripping off the environment string
-		String [] theArgs = new String [nArgs];
-		for (lcv = 0; lcv < nArgs; lcv++)
-			{
-			theArgs[lcv] = progArray[lcv + 1];
-			}
-		
-		try 
-			{
-			String [] envStrings = sEnvString.split(",");
-			Map<String, String> newEnv = new HashMap<String, String>();
-			
-			for (lcv = 0; lcv < envStrings.length; lcv++)
-				{
-				temp = envStrings[lcv].indexOf("=");
-				if (temp > 0)
-					{
-					newEnv.put(	envStrings[lcv].substring(0, temp), 
-								envStrings[lcv].substring(temp + 1, envStrings[lcv].length()));
-					}
-				}
-			
-			Map<String, String> sysEnv = System.getenv();
-			
-			nArraySize = sysEnv.size();
-			
-			for (Map.Entry<String, String> entry : newEnv.entrySet())
-				{
-				if (!sysEnv.containsKey(entry.getKey()))
-					{
-					nArraySize++;
-					}
-				}
-			
-			String[] envArray = new String[nArraySize];
-				
-			int		i = 0;
-			int		offset;
-			String	sKey = "";
-			String 	sValue = "";
-			
-	        for (Map.Entry<String, String> entry : sysEnv.entrySet())
-	        	{
-	        	sKey = entry.getKey();
-	        	if (newEnv.containsKey(sKey))
-	        		{
-	        		sValue = newEnv.get(sKey);
-	        		if ((offset = sValue.indexOf("$" + sKey)) != -1)
-	        			{
-	        			envArray[i++] = sKey + 
-	        							"=" + 
-	        							sValue.substring(0, offset) + 
-	        							entry.getValue() + 
-	        							sValue.substring(offset + sKey.length() + 1);
-	        			}
-	        		else
-	        			envArray[i++] = sKey + "=" + sValue;
-	        		newEnv.remove(sKey);
-	        		}
-	        	else
-	        		envArray[i++] = entry.getKey() + "=" + entry.getValue();
-	        	}
-	        
-			for (Map.Entry<String, String> entry : newEnv.entrySet())
-				{
-        		envArray[i++] = entry.getKey() + "=" + entry.getValue();
-				}
-	        
-			pProc = Runtime.getRuntime().exec(theArgs, envArray);
-
-			RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
-			outThrd.start();
-			outThrd.join(10000);
-			int nRetCode = pProc.exitValue();
-			sRet = "return code [" + nRetCode + "]";
-			}
-		catch(UnsupportedOperationException e)
-			{
-			if (e != null)
-				e.printStackTrace();
-			}
-		catch(ClassCastException e)
-			{
-			if (e != null)
-				e.printStackTrace();
-			}
-		catch(IllegalArgumentException e)
-			{
-			if (e != null)
-				e.printStackTrace();
-			}
-		catch(NullPointerException e)
-			{
-			if (e != null)
-				e.printStackTrace();
-			}
-		catch (IOException e) 
-			{
-			e.printStackTrace();
-			}
-		catch (InterruptedException e)
-			{
-			e.printStackTrace();
-			sRet = "Timed out!";
-			}
-
-		return (sRet);
-		}
-/*	
-	public String InstallApplication()
-		{
-		String sRet = "";
-		String sFileName = Environment.getExternalStorageDirectory() + "/org.mozilla.fennec.apk";
-		
-		Intent instIntent = new Intent();
-		
-		instIntent.setAction(android.content.Intent.ACTION_VIEW);
-		instIntent.setDataAndType(Uri.fromFile(new File(sFileName)), "application/vnd.android.package-archive");
-//		instIntent.setDataAndType(Uri.parse("file:///sdcard/org.mozilla.fennec.apk"), "application/vnd.android.package-archive");
-		SUTAgentAndroid.me.startActivity(instIntent);
-		
-//		Instrumentation inst = new Instrumentation();
-//		inst.sendKeyDownUpSync(KeyEvent.KEYCODE_SOFT_LEFT);
-		
-		return(sRet);
-		}
-
-	public String UninstallApplication()
-		{
-		String sRet = "";
-		Uri		pkgURI = Uri.parse("package:" + "org.mozilla.fennec");
-	
-		Intent unInstIntent = new Intent(Intent.ACTION_DELETE, pkgURI);
-	
-		SUTAgentAndroid.me.startActivity(unInstIntent);
-
-		return(sRet);
-		}
-*/
-	private String PrintUsage()
-		{
-		String sRet = 
-			"run [executable] [args]      - start program no wait\n" +
-			"exec [executable] [args]     - start program wait\n" +
-			"fire [executable] [args]     - start program no wait\n" +
-			"envrun [env pairs] [cmdline] - start program no wait\n" +
-			"kill [program name]          - kill program no path\n" +
-			"killall                      - kill all processes started\n" +
-			"ps                           - list of running processes\n" +
-			"info                         - list of device info\n" +
-			"        [os]                 - os version for device\n" +
-			"        [id]                 - unique identifier for device\n" +
-			"        [uptime]             - uptime for device\n" +
-			"        [systime]            - current system time on device\n" +
-			"        [screen]             - width, height and bits per pixel for device\n" +
-			"        [memory]             - physical, free, available, storage memory for device\n" +
-			"        [processes]          - list of running processes see 'ps'\n" +
-			"deadman timeout              - set the duration for the deadman timer\n" +
-			"alrt [on/off]                - start or stop sysalert behavior\n" +
-			"disk [arg]                   - prints disk space info\n" +
-			"cp file1 file2               - copy file1 to file2 on device\n" +
-			"time file                    - timestamp for file on device\n" +
-			"hash file                    - generate hash for file on device\n" +
-			"cd directory                 - change cwd on device\n" +
-			"cat file                     - cat file on device\n" +
-			"cwd                          - display cwd on device\n" +
-			"mv file1 file2               - move file1 to file2 on device\n" +
-			"push filename                - push file to device\n" +
-			"rm file                      - delete file on device\n" +
-			"rmdr directory               - delete directory on device even if not empty\n" +
-			"mkdr directory               - create directory on device\n" +
-			"dirw directory               - tests whether the directory is writable on the device\n" +
-			"stat processid               - stat process on device\n" +
-			"dead processid               - print whether the process is alive or hung on device\n" +
-			"mems                         - dump memory stats on device\n" +
-			"ls                           - print directory on device\n" +
-			"tmpd                         - print temp directory on device\n" +
-			"ping [hostname/ipaddr]       - ping a network device\n" +
-			"unzp zipfile destdir         - unzip the zipfile into the destination dir\n" +
-			"zip zipfile src              - zip the source file/dir into zipfile\n" +
-			"rebt                         - reboot device\n" +
-			"inst /path/filename.apk      - install the referenced apk file\n" +
-			"uninst packagename           - uninstall the referenced package\n" +
-			"updt pkgname pkgfile         - unpdate the referenced package\n" +
-			"clok                         - the current device time expressed as the number of millisecs since epoch\n" +
-			"settime date time            - sets the device date and time (YYYY/MM/DD HH:MM:SS)\n" +
-			"tzset timezone               - sets the device timezone format is GMTxhh:mm x = +/- or a recognized Olsen string\n" +
-			"tzget                        - returns the current timezone set on the device\n" +
-			"rebt                         - reboot device\n" +
-			"quit                         - disconnect SUTAgent\n" +
-			"exit                         - close SUTAgent\n" +
-			"ver                          - SUTAgent version\n" +
-			"help                         - you're reading it";
-		return (sRet);
-		}
-}
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android SUTAgent code.
+ *
+ * The Initial Developer of the Original Code is
+ * Bob Moss.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package com.mozilla.SUTAgentAndroid.service;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.zip.Adler32;
+import java.util.zip.CheckedInputStream;
+import java.util.zip.CheckedOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPReply;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import com.mozilla.SUTAgentAndroid.NtpMessage;
+import com.mozilla.SUTAgentAndroid.R;
+import com.mozilla.SUTAgentAndroid.SUTAgentAndroid;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Debug;
+import android.os.Environment;
+import android.os.StatFs;
+import android.os.SystemClock;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.WindowManager;
+
+public class DoCommand {
+
+    String lineSep = System.getProperty("line.separator");
+    Process    pProc;
+    OutputStream sutIn;
+    InputStream    sutErr;
+    InputStream    sutOut;
+    AlertLooperThread alrt = null;
+    ContextWrapper    contextWrapper = null;
+
+    String    currentDir = "/";
+    String    sErrorPrefix = "##AGENT-WARNING## ";
+    boolean bTraceOn = false;
+
+    String ffxProvider = "org.mozilla.ffxcp";
+    String fenProvider = "org.mozilla.fencp";
+
+    private final String prgVersion = "SUTAgentAndroid Version 1.01";
+
+    public enum Command
+        {
+        RUN ("run"),
+        EXEC ("exec"),
+        ENVRUN ("envrun"),
+        KILL ("kill"),
+        PS ("ps"),
+        DEVINFO ("info"),
+        OS ("os"),
+        ID ("id"),
+        UPTIME ("uptime"),
+        SETTIME ("settime"),
+        SYSTIME ("systime"),
+        SCREEN ("screen"),
+        MEMORY ("memory"),
+        POWER ("power"),
+        PROCESS ("process"),
+        GETAPPROOT ("getapproot"),
+        TESTROOT ("testroot"),
+        ALRT ("alrt"),
+        DISK ("disk"),
+        CP ("cp"),
+        TIME ("time"),
+        HASH ("hash"),
+        CD ("cd"),
+        CAT ("cat"),
+        CWD ("cwd"),
+        MV ("mv"),
+        PUSH ("push"),
+        PULL ("pull"),
+        RM ("rm"),
+        PRUNE ("rmdr"),
+        MKDR ("mkdr"),
+        DIRWRITABLE ("dirw"),
+        ISDIR ("isdir"),
+        DEAD ("dead"),
+        MEMS ("mems"),
+        LS ("ls"),
+        TMPD ("tmpd"),
+        PING ("ping"),
+        REBT ("rebt"),
+        UNZP ("unzp"),
+        ZIP ("zip"),
+        CLOK ("clok"),
+        STAT ("stat"),
+        QUIT ("quit"),
+        EXIT ("exit"),
+        HELP ("help"),
+        FTPG ("ftpg"),
+        FTPP ("ftpp"),
+        INST ("inst"),
+        UPDT ("updt"),
+        UNINST ("uninst"),
+        TEST ("test"),
+        DBG ("dbg"),
+        TRACE ("trace"),
+        VER ("ver"),
+        TZGET ("tzget"),
+        TZSET ("tzset"),
+        ADB ("adb"),
+        UNKNOWN ("unknown");
+
+        private final String theCmd;
+
+        Command(String theCmd) { this.theCmd = theCmd; }
+
+        public String theCmd() {return theCmd;}
+
+        public static Command getCmd(String sCmd)
+            {
+            Command retCmd = UNKNOWN;
+            for (Command cmd : Command.values())
+                {
+                if (cmd.theCmd().equalsIgnoreCase(sCmd))
+                    {
+                    retCmd = cmd;
+                    break;
+                    }
+                }
+            return (retCmd);
+            }
+        }
+
+    public DoCommand(ContextWrapper service)
+        {
+        this.contextWrapper = service;
+        }
+
+    public String processCommand(String theCmdLine, PrintWriter out, BufferedInputStream in, OutputStream cmdOut)
+        {
+        String     strReturn = "";
+        Command    cCmd = null;
+        Command cSubCmd = null;
+
+        if (bTraceOn)
+            ((ASMozStub)this.contextWrapper).SendToDataChannel(theCmdLine);
+
+        String [] Argv = parseCmdLine2(theCmdLine);
+
+        int Argc = Argv.length;
+
+        cCmd = Command.getCmd(Argv[0]);
+
+        switch(cCmd)
+            {
+            case TRACE:
+                if (Argc == 2)
+                    bTraceOn = (Argv[1].equalsIgnoreCase("on") ? true : false);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for trace command!";
+                break;
+
+            case VER:
+                strReturn = prgVersion;
+                break;
+
+            case CLOK:
+                strReturn = GetClok();
+                break;
+
+            case TZGET:
+                strReturn = GetTimeZone();
+                break;
+
+            case TZSET:
+                if (Argc == 2)
+                    strReturn = SetTimeZone(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for settz command!";
+                break;
+
+            case UPDT:
+                if (Argc >= 2)
+                    strReturn = StrtUpdtOMatic(Argv[1], Argv[2], (Argc > 3 ? Argv[3] : null), (Argc > 4 ? Argv[4] : null));
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for updt command!";
+                break;
+
+            case SETTIME:
+                strReturn = SetSystemTime(Argv[1], (Argc > 2 ? Argv[2] : null), cmdOut);
+                break;
+
+            case CWD:
+                try {
+                    strReturn = new java.io.File(currentDir).getCanonicalPath();
+                    }
+                catch (IOException e)
+                    {
+                    e.printStackTrace();
+                    }
+                break;
+
+            case CD:
+                if (Argc == 2)
+                    strReturn = changeDir(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for cd command!";
+                break;
+
+            case LS:
+                strReturn = PrintDir(((Argc > 1) ? Argv[1] : currentDir));
+                break;
+
+            case GETAPPROOT:
+                if (Argc == 2)
+                    strReturn = GetAppRoot(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for getapproot command!";
+                break;
+
+            case ISDIR:
+                if (Argc == 2)
+                    strReturn = isDirectory(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for isdir command!";
+                break;
+
+            case TESTROOT:
+                strReturn = GetTestRoot();
+                break;
+
+            case DEAD:
+                if (Argc == 2)
+                    strReturn = (IsProcessDead(Argv[1]) ? (Argv[1] + " is hung or unresponsive") : (Argv[1] + " is ok"));
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for dead command!";
+                break;
+
+            case PS:
+                strReturn = GetProcessInfo();
+                break;
+
+            case PULL:
+                if (Argc >= 2) {
+                    long lOff = 0;
+                    long lLen = -1;
+                    if (Argc > 2) {
+                        try {
+                            lOff = Long.parseLong(Argv[2].trim());
+                            System.out.println("offset = " + lOff);
+                        } catch (NumberFormatException nfe) {
+                            lOff = 0;
+                            System.out.println("NumberFormatException: " + nfe.getMessage());
+                        }
+                    }
+                    if (Argc == 4) {
+                        try {
+                            lLen = Long.parseLong(Argv[3].trim());
+                            System.out.println("length = " + lLen);
+                        } catch (NumberFormatException nfe) {
+                            lLen = -1;
+                            System.out.println("NumberFormatException: " + nfe.getMessage());
+                        }
+                    }
+                    strReturn = Pull(Argv[1], lOff, lLen, cmdOut);
+                } else {
+                    strReturn = sErrorPrefix + "Wrong number of arguments for pull command!";
+                }
+                break;
+
+            case PUSH:
+                if (Argc == 3)
+                    {
+                    long lArg = 0;
+                    try
+                        {
+                        lArg = Long.parseLong(Argv[2].trim());
+                        System.out.println("long l = " + lArg);
+                        }
+                    catch (NumberFormatException nfe)
+                        {
+                        System.out.println("NumberFormatException: " + nfe.getMessage());
+                        }
+
+                    strReturn = Push(Argv[1], in, lArg);
+                    }
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for push command!";
+                break;
+
+            case INST:
+                if (Argc >= 2)
+                    strReturn = InstallApp(Argv[1], cmdOut);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for inst command!";
+                break;
+
+            case UNINST:
+                if (Argc >= 2)
+                    strReturn = UnInstallApp(Argv[1], cmdOut);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for inst command!";
+                break;
+
+            case ALRT:
+                if (Argc > 1)
+                    {
+                    if (Argv[1].contentEquals("on"))
+                        {
+                        String sTitle = "Agent Alert";
+                        String sMsg = "The Agent Alert System has been activated!";
+                        if (Argc == 3) {
+                            sTitle = Argv[2];
+                            sMsg = "";
+                        } else if (Argc == 4) {
+                            sTitle = Argv[2];
+                            sMsg = Argv[3];
+                        }
+                        StartAlert(sTitle, sMsg);
+                        }
+                    else
+                        {
+                        StopAlert();
+                        }
+                    }
+                else
+                    {
+                    strReturn = sErrorPrefix + "Wrong number of arguments for alrt command!";
+                    }
+                break;
+
+            case REBT:
+                if (Argc >= 1)
+                    strReturn = RunReboot(cmdOut, (Argc > 1 ? Argv[1] : null), (Argc > 2 ? Argv[2] : null));
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for rebt command!";
+//                RunReboot(cmdOut);
+                break;
+
+            case TMPD:
+                strReturn = GetTmpDir();
+                break;
+
+            case DEVINFO:
+                if (Argc == 1)
+                    {
+                    strReturn += SUTAgentAndroid.sUniqueID;
+                    strReturn += "\n";
+                    strReturn += GetOSInfo();
+                    strReturn += "\n";
+                    strReturn += GetSystemTime();
+                    strReturn += "\n";
+                    strReturn += GetUptime();
+                    strReturn += "\n";
+                    strReturn += GetScreenInfo();
+                    strReturn += "\n";
+                    strReturn += GetMemoryInfo();
+                    strReturn += "\n";
+                    strReturn += GetPowerInfo();
+                    strReturn += "\n";
+                    strReturn += GetProcessInfo();
+                    }
+                else
+                    {
+                    cSubCmd = Command.getCmd(Argv[1]);
+                    switch(cSubCmd)
+                        {
+                        case ID:
+                            strReturn = SUTAgentAndroid.sUniqueID;
+                            break;
+
+                        case SCREEN:
+                            strReturn = GetScreenInfo();
+                            break;
+
+                        case PROCESS:
+                            strReturn = GetProcessInfo();
+                            break;
+
+                        case OS:
+                            strReturn = GetOSInfo();
+                            break;
+
+                        case SYSTIME:
+                            strReturn = GetSystemTime();
+                            break;
+
+                        case UPTIME:
+                            strReturn = GetUptime();
+                            break;
+
+                        case MEMORY:
+                            strReturn = GetMemoryInfo();
+                            break;
+
+                        case POWER:
+                            strReturn += GetPowerInfo();
+                            break;
+
+                        default:
+                            break;
+                        }
+                    }
+                break;
+
+            case STAT:
+                if (Argc == 2)
+                    strReturn = StatProcess(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for ping command!";
+                break;
+
+            case PING:
+                if (Argc == 2)
+                    strReturn = SendPing(Argv[1], cmdOut);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for ping command!";
+                break;
+
+            case HASH:
+                if (Argc == 2)
+                    strReturn = HashFile(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for hash command!";
+                break;
+
+            case PRUNE:
+                if (Argc == 2)
+                    strReturn = PruneDir(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for prune command!";
+                break;
+
+            case FTPG:
+                if (Argc == 4)
+                    strReturn = FTPGetFile(Argv[1], Argv[2], Argv[3], cmdOut);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for ftpg command!";
+                break;
+
+            case CAT:
+                if (Argc == 2)
+                    strReturn = Cat(Argv[1], cmdOut);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for cat command!";
+                break;
+
+            case DIRWRITABLE:
+                if (Argc == 2)
+                    strReturn = IsDirWritable(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for dirwritable command!";
+                break;
+
+            case TIME:
+                if (Argc == 2)
+                    strReturn = PrintFileTimestamp(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for time command!";
+                break;
+
+            case MKDR:
+                if (Argc == 2)
+                    strReturn = MakeDir(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for mkdr command!";
+                break;
+
+            case RM:
+                if (Argc == 2)
+                    strReturn = RemoveFile(Argv[1]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for rm command!";
+                break;
+
+            case MV:
+                if (Argc == 3)
+                    strReturn = Move(Argv[1], Argv[2]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for mv command!";
+                break;
+
+            case CP:
+                if (Argc == 3)
+                    strReturn = CopyFile(Argv[1], Argv[2]);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for cp command!";
+                break;
+
+            case QUIT:
+            case EXIT:
+                strReturn = Argv[0];
+                break;
+
+            case DBG:
+                Debug.waitForDebugger();
+                strReturn = "waitForDebugger on";
+                break;
+
+            case ADB:
+                if (Argc == 2) {
+                    if (Argv[1].contains("ip") || Argv[1].contains("usb")) {
+                        strReturn = SetADB(Argv[1]);
+                    } else {
+                        strReturn = sErrorPrefix + "Unrecognized argument for adb command!";
+                    }
+                } else {
+                    strReturn = sErrorPrefix + "Wrong number of arguments for adb command!";
+                }
+                break;
+
+            case TEST:
+                long lFreeMemory = Runtime.getRuntime().freeMemory();
+                long lTotMemory = Runtime.getRuntime().totalMemory();
+                long lMaxMemory = Runtime.getRuntime().maxMemory();
+
+
+                if (lFreeMemory > 0) {
+                    strReturn = "Max memory: " + lMaxMemory + "\nTotal Memory: " + lTotMemory + "\nFree memory: " + lFreeMemory;
+                    break;
+                }
+
+                ContentResolver cr = contextWrapper.getContentResolver();
+                Uri ffxFiles = null;
+
+                if (Argv[1].contains("fennec")) {
+                    ffxFiles = Uri.parse("content://" + fenProvider + "/dir");
+                } else if (Argv[1].contains("firefox")) {
+                    ffxFiles = Uri.parse("content://" + ffxProvider + "/dir");
+                }
+
+//                Uri ffxFiles = Uri.parse("content://org.mozilla.fencp/file");
+                String[] columns = new String[] {
+                        "_id",
+                        "isdir",
+                        "filename",
+                        "length"
+                    };
+//                String[] columns = new String[] {
+//                        "_id",
+//                        "chunk"
+//                     };
+                Cursor myCursor = cr.query(    ffxFiles,
+                                            columns,                         // Which columns to return
+                                            (Argc > 1 ? Argv[1] : null),    // Which rows to return (all rows)
+                                            null,                           // Selection arguments (none)
+                                            null);                            // Put the results in ascending order by name
+/*
+                if (myCursor != null) {
+                    int nRows = myCursor.getCount();
+                    String [] colNames = myCursor.getColumnNames();
+                    int    nID = 0;
+                    int nBytesRecvd = 0;
+
+                    for (int lcv = 0; lcv < nRows; lcv++) {
+                        if  (myCursor.moveToPosition(lcv)) {
+                            nID = myCursor.getInt(0);
+                            byte [] buf = myCursor.getBlob(1);
+                            if (buf != null) {
+                                nBytesRecvd += buf.length;
+                                strReturn += new String(buf);
+                                buf = null;
+                            }
+                        }
+                    }
+                    strReturn += "[eof - " + nBytesRecvd + "]";
+                    myCursor.close();
+                }
+
+*/
+                if (myCursor != null)
+                    {
+                    int nRows = myCursor.getCount();
+                    int    nID = 0;
+                    String sFileName = "";
+                    long lFileSize = 0;
+                    boolean bIsDir = false;
+
+                    for (int lcv = 0; lcv < nRows; lcv++)
+                        {
+                        if  (myCursor.moveToPosition(lcv))
+                            {
+                            nID = myCursor.getInt(0);
+                            bIsDir = (myCursor.getInt(1) == 1 ? true : false);
+                            sFileName = myCursor.getString(2);
+                            lFileSize = myCursor.getLong(3);
+                            strReturn += "" + nID + "\t" + (bIsDir ? "<dir> " : "      ") + sFileName + "\t" + lFileSize + "\n";
+                            }
+                        }
+                    myCursor.close();
+                    }
+                break;
+
+            case EXEC:
+            case ENVRUN:
+                if (Argc >= 2)
+                    {
+                    String [] theArgs = new String [Argc - 1];
+
+                    for (int lcv = 1; lcv < Argc; lcv++)
+                        {
+                        theArgs[lcv - 1] = Argv[lcv];
+                        }
+
+                    strReturn = StartPrg2(theArgs, cmdOut);
+                    }
+                else
+                    {
+                    strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!";
+                    }
+                break;
+
+            case RUN:
+                if (Argc >= 2)
+                    {
+                    String [] theArgs = new String [Argc - 1];
+
+                    for (int lcv = 1; lcv < Argc; lcv++)
+                        {
+                        theArgs[lcv - 1] = Argv[lcv];
+                        }
+
+                    if (Argv[1].contains("/") || Argv[1].contains("\\") || !Argv[1].contains("."))
+                        strReturn = StartPrg(theArgs, cmdOut);
+                    else
+                        strReturn = StartJavaPrg(theArgs, null);
+                    }
+                else
+                    {
+                    strReturn = sErrorPrefix + "Wrong number of arguments for " + Argv[0] + " command!";
+                    }
+                break;
+
+            case KILL:
+                if (Argc == 2)
+                    strReturn = KillProcess(Argv[1], cmdOut);
+                else
+                    strReturn = sErrorPrefix + "Wrong number of arguments for kill command!";
+                break;
+
+            case DISK:
+                strReturn = GetDiskInfo((Argc == 2 ? Argv[1] : "/"));
+                break;
+
+            case UNZP:
+                strReturn = Unzip(Argv[1], (Argc == 3 ? Argv[2] : ""));
+                break;
+
+            case ZIP:
+                strReturn = Zip(Argv[1], (Argc == 3 ? Argv[2] : ""));
+                break;
+
+            case HELP:
+                strReturn = PrintUsage();
+                break;
+
+            default:
+                strReturn = sErrorPrefix + "[" + Argv[0] + "] command";
+                if (Argc > 1)
+                    {
+                    strReturn += " with arg(s) =";
+                    for (int lcv = 1; lcv < Argc; lcv++)
+                        {
+                        strReturn += " [" + Argv[lcv] + "]";
+                        }
+                    }
+                strReturn += " is currently not implemented.";
+                break;
+            }
+
+        return(strReturn);
+        }
+
+    private void SendNotification(String tickerText, String expandedText) {
+        NotificationManager notificationManager = (NotificationManager)contextWrapper.getSystemService(Context.NOTIFICATION_SERVICE);
+        int icon = R.drawable.ateamlogo;
+        long when = System.currentTimeMillis();
+
+        Notification notification = new Notification(icon, tickerText, when);
+
+        notification.flags |= (Notification.FLAG_INSISTENT | Notification.FLAG_AUTO_CANCEL);
+        notification.defaults |= Notification.DEFAULT_SOUND;
+        notification.defaults |= Notification.DEFAULT_VIBRATE;
+        notification.defaults |= Notification.DEFAULT_LIGHTS;
+
+        Context context = contextWrapper.getApplicationContext();
+
+        // Intent to launch an activity when the extended text is clicked
+        Intent intent2 = new Intent(contextWrapper, SUTAgentAndroid.class);
+        PendingIntent launchIntent = PendingIntent.getActivity(context, 0, intent2, 0);
+
+        notification.setLatestEventInfo(context, tickerText, expandedText, launchIntent);
+
+        notificationManager.notify(1959, notification);
+    }
+
+private void CancelNotification()
+    {
+    NotificationManager notificationManager = (NotificationManager)contextWrapper.getSystemService(Context.NOTIFICATION_SERVICE);
+    notificationManager.cancel(1959);
+    }
+
+    public void StartAlert(String sTitle, String sMsg)
+        {
+        // start the alert message
+        SendNotification(sTitle, sMsg);
+        }
+
+    public void StopAlert()
+        {
+        CancelNotification();
+        }
+
+    public String [] parseCmdLine2(String theCmdLine)
+        {
+        String    cmdString;
+        String    workingString;
+        String    workingString2;
+        String    workingString3;
+        List<String> lst = new ArrayList<String>();
+        int nLength = 0;
+        int nFirstSpace = -1;
+
+        // Null cmd line
+        if (theCmdLine == null)
+            {
+            String [] theArgs = new String [1];
+            theArgs[0] = new String("");
+            return(theArgs);
+            }
+        else
+            {
+            nLength = theCmdLine.length();
+            nFirstSpace = theCmdLine.indexOf(' ');
+            }
+
+        if (nFirstSpace == -1)
+            {
+            String [] theArgs = new String [1];
+            theArgs[0] = new String(theCmdLine);
+            return(theArgs);
+            }
+
+        // Get the command
+        cmdString = new String(theCmdLine.substring(0, nFirstSpace));
+        lst.add(cmdString);
+
+        // Jump past the command and trim
+        workingString = (theCmdLine.substring(nFirstSpace + 1, nLength)).trim();
+
+        while ((nLength = workingString.length()) > 0)
+            {
+            int nEnd = 0;
+            int    nStart = 0;
+
+            // if we have a quote
+            if (workingString.startsWith("\""))
+                {
+                // point to the first non quote char
+                nStart = 1;
+                // find the matching quote
+                nEnd = workingString.indexOf('"', nStart);
+
+                char prevChar;
+
+                while(nEnd != -1)
+                    {
+                    // check to see if the quotation mark has been escaped
+                    prevChar = workingString.charAt(nEnd - 1);
+                    if (prevChar == '\\')
+                        {
+                        // if escaped, point past this quotation mark and find the next
+                        nEnd++;
+                        if (nEnd < nLength)
+                            nEnd = workingString.indexOf('"', nEnd);
+                        else
+                            nEnd = -1;
+                        }
+                    else
+                        break;
+                    }
+
+                // there isn't one
+                if (nEnd == -1)
+                    {
+                    // point at the quote
+                    nStart = 0;
+                    // so find the next space
+                    nEnd = workingString.indexOf(' ', nStart);
+                    // there isn't one of those either
+                    if (nEnd == -1)
+                        nEnd = nLength;    // Just grab the rest of the cmdline
+                    }
+                }
+            else // no quote so find the next space
+                {
+                nEnd = workingString.indexOf(' ', nStart);
+                // there isn't one of those
+                if (nEnd == -1)
+                    nEnd = nLength;    // Just grab the rest of the cmdline
+                }
+
+            // get the substring
+            workingString2 = workingString.substring(nStart, nEnd);
+
+            // if we have escaped quotes
+            if (workingString2.contains("\\\""))
+                {
+                do
+                    {
+                    // replace escaped quote with embedded quote
+                    workingString3 = workingString2.replace("\\\"", "\"");
+                    workingString2 = workingString3;
+                    }
+                while(workingString2.contains("\\\""));
+                }
+
+            // add it to the list
+            lst.add(new String(workingString2));
+
+            // if we are dealing with a quote
+            if (nStart > 0)
+                nEnd++; //  point past the end one
+
+            // jump past the substring and trim it
+            workingString = (workingString.substring(nEnd)).trim();
+            }
+
+        // ok we're done package up the results
+        int nItems = lst.size();
+
+        String [] theArgs = new String [nItems];
+
+        for (int lcv = 0; lcv < nItems; lcv++)
+            {
+            theArgs[lcv] = lst.get(lcv);
+            }
+
+        return(theArgs);
+        }
+
+    public String [] parseCmdLine(String theCmdLine) {
+        String    cmdString;
+        String    workingString;
+        String    workingString2;
+        List<String> lst = new ArrayList<String>();
+        int nLength = 0;
+        int nFirstSpace = -1;
+
+        // Null cmd line
+        if (theCmdLine == null)
+            {
+            String [] theArgs = new String [1];
+            theArgs[0] = new String("");
+            return(theArgs);
+            }
+        else
+            {
+            nLength = theCmdLine.length();
+            nFirstSpace = theCmdLine.indexOf(' ');
+            }
+
+        if (nFirstSpace == -1)
+            {
+            String [] theArgs = new String [1];
+            theArgs[0] = new String(theCmdLine);
+            return(theArgs);
+            }
+
+        // Get the command
+        cmdString = new String(theCmdLine.substring(0, nFirstSpace));
+        lst.add(cmdString);
+
+        // Jump past the command and trim
+        workingString = (theCmdLine.substring(nFirstSpace + 1, nLength)).trim();
+
+        while ((nLength = workingString.length()) > 0)
+            {
+            int nEnd = 0;
+            int    nStart = 0;
+
+            // if we have a quote
+            if (workingString.startsWith("\""))
+                {
+                // point to the first non quote char
+                nStart = 1;
+                // find the matching quote
+                nEnd = workingString.indexOf('"', nStart);
+                // there isn't one
+                if (nEnd == -1)
+                    {
+                    // point at the quote
+                    nStart = 0;
+                    // so find the next space
+                    nEnd = workingString.indexOf(' ', nStart);
+                    // there isn't one of those either
+                    if (nEnd == -1)
+                        nEnd = nLength;    // Just grab the rest of the cmdline
+                    }
+                else
+                    {
+                    nStart = 0;
+                    nEnd++;
+                    }
+                }
+            else // no quote so find the next space
+                {
+                nEnd = workingString.indexOf(' ', nStart);
+
+                // there isn't one of those
+                if (nEnd == -1)
+                    nEnd = nLength;    // Just grab the rest of the cmdline
+                }
+
+            // get the substring
+            workingString2 = workingString.substring(nStart, nEnd);
+
+            // add it to the list
+            lst.add(new String(workingString2));
+
+            // jump past the substring and trim it
+            workingString = (workingString.substring(nEnd)).trim();
+            }
+
+        int nItems = lst.size();
+
+        String [] theArgs = new String [nItems];
+
+        for (int lcv = 0; lcv < nItems; lcv++)
+            {
+            theArgs[lcv] = lst.get(lcv);
+            }
+
+        return(theArgs);
+        }
+
+    public String fixFileName(String fileName)
+        {
+        String    sRet = "";
+        String    sTmpFileName = "";
+
+        sRet = fileName.replace('\\', '/');
+
+        if (sRet.startsWith("/"))
+            sTmpFileName = sRet;
+        else
+            sTmpFileName = currentDir + "/" + sRet;
+
+        sRet = sTmpFileName.replace('\\', '/');
+        sTmpFileName = sRet;
+        sRet = sTmpFileName.replace("//", "/");
+
+        return(sRet);
+        }
+
+    public String AddFilesToZip(ZipOutputStream out, String baseDir, String relDir)
+    {
+        final int             BUFFER     = 2048;
+        String                sRet    = "";
+        String                 curDir     = "";
+        String                relFN    = "";
+        BufferedInputStream origin = null;
+        byte                 data[] = new byte[BUFFER];
+
+        if (relDir.length() > 0)
+            curDir = baseDir + "/" + relDir;
+        else
+            curDir = baseDir;
+
+        File f = new File(curDir);
+
+        if (f.isFile())
+            {
+            try {
+                relFN = ((relDir.length() > 0) ? relDir + "/" + f.getName() : f.getName());
+                System.out.println("Adding: "+relFN);
+                sRet += "Adding: "+    relFN + lineSep;
+                FileInputStream fi = new FileInputStream(curDir);
+                origin = new BufferedInputStream(fi, BUFFER);
+                ZipEntry entry = new ZipEntry(relFN);
+                out.putNextEntry(entry);
+                int count;
+                while((count = origin.read(data, 0, BUFFER)) != -1)
+                    {
+                    out.write(data, 0, count);
+                    }
+                origin.close();
+                }
+            catch(Exception e)
+                {
+                e.printStackTrace();
+                }
+
+            return(sRet);
+            }
+
+        String    files[] = f.list();
+
+        if (files != null)
+            {
+            try {
+                for(int i = 0; i < files.length; i++)
+                    {
+                    f = new File(curDir + "/" + files[i]);
+                    if (f.isDirectory())
+                        {
+                        if (relDir.length() > 0)
+                            sRet += AddFilesToZip(out, baseDir, relDir + "/" + files[i]);
+                        else
+                            sRet += AddFilesToZip(out, baseDir, files[i]);
+                        }
+                    else
+                        {
+                        relFN = ((relDir.length() > 0) ? relDir + "/" + files[i] : files[i]);
+                        System.out.println("Adding: "+relFN);
+                        sRet += "Adding: "+    relFN + lineSep;
+                        FileInputStream fi = new FileInputStream(curDir + "/" + files[i]);
+                        origin = new BufferedInputStream(fi, BUFFER);
+                        ZipEntry entry = new ZipEntry(relFN);
+                        out.putNextEntry(entry);
+                        int count;
+                        while((count = origin.read(data, 0, BUFFER)) != -1)
+                            {
+                            out.write(data, 0, count);
+                            }
+                        origin.close();
+                        }
+                    }
+                }
+            catch(Exception e)
+                {
+                e.printStackTrace();
+                }
+            }
+
+        return(sRet);
+    }
+
+    public String Zip(String zipFileName, String srcName)
+        {
+        String    fixedZipFileName = fixFileName(zipFileName);
+        String    fixedSrcName = fixFileName(srcName);
+        String sRet = "";
+
+        try {
+            FileOutputStream dest = new FileOutputStream(fixedZipFileName);
+            CheckedOutputStream checksum = new CheckedOutputStream(dest, new Adler32());
+            ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(checksum));
+            out.setMethod(ZipOutputStream.DEFLATED);
+
+            sRet += AddFilesToZip(out, fixedSrcName, "");
+
+            out.close();
+            System.out.println("checksum:                   "+checksum.getChecksum().getValue());
+            sRet += "checksum:                   "+checksum.getChecksum().getValue();
+            }
+        catch(Exception e)
+            {
+            e.printStackTrace();
+            }
+
+        return(sRet);
+    }
+
+    public String Unzip(String zipFileName, String dstDirectory)
+        {
+        String     sRet = "";
+        String    fixedZipFileName = fixFileName(zipFileName);
+        String    fixedDstDirectory = fixFileName(dstDirectory);
+        String    dstFileName = "";
+        int        nNumExtracted = 0;
+        boolean bRet = false;
+
+        try {
+            final int BUFFER = 2048;
+            BufferedOutputStream dest = null;
+            ZipFile zipFile = new ZipFile(fixedZipFileName);
+            int nNumEntries = zipFile.size();
+            zipFile.close();
+
+            FileInputStream fis = new FileInputStream(fixedZipFileName);
+            CheckedInputStream checksum = new CheckedInputStream(fis, new Adler32());
+            ZipInputStream zis = new ZipInputStream(new BufferedInputStream(checksum));
+            ZipEntry entry;
+
+            byte [] data = new byte[BUFFER];
+
+            while((entry = zis.getNextEntry()) != null)
+                {
+                System.out.println("Extracting: " + entry);
+                int count;
+                if (fixedDstDirectory.length() > 0)
+                    dstFileName = fixedDstDirectory + entry.getName();
+                else
+                    dstFileName = entry.getName();
+
+                String tmpDir = dstFileName.substring(0, dstFileName.lastIndexOf('/'));
+                File tmpFile = new File(tmpDir);
+                if (!tmpFile.exists())
+                    {
+                    bRet = tmpFile.mkdirs();
+                    }
+                else
+                    bRet = true;
+
+                if (bRet)
+                    {
+                    // if we aren't just creating a directory
+                    if (dstFileName.lastIndexOf('/') != (dstFileName.length() - 1))
+                        {
+                        // write out the file
+                        FileOutputStream fos = new FileOutputStream(dstFileName);
+                        dest = new BufferedOutputStream(fos, BUFFER);
+                        while ((count = zis.read(data, 0, BUFFER)) != -1)
+                            {
+                            dest.write(data, 0, count);
+                            }
+                        dest.flush();
+                        dest.close();
+                        dest = null;
+                        fos.close();
+                        fos = null;
+                        }
+                    nNumExtracted++;
+                    }
+                else
+                    sRet += " - failed" + lineSep;
+                }
+
+            data = null;
+            zis.close();
+            System.out.println("Checksum:          "+checksum.getChecksum().getValue());
+            sRet += "Checksum:          "+checksum.getChecksum().getValue();
+            sRet += lineSep + nNumExtracted + " of " + nNumEntries + " sucessfully extracted";
+            }
+        catch(Exception e)
+            {
+            e.printStackTrace();
+            }
+
+        return(sRet);
+        }
+
+    public String StatProcess(String string)
+        {
+        String sRet = "";
+        ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
+        int    [] nPids = new int [1];
+
+        nPids[0] = Integer.parseInt(string);
+
+        android.os.Debug.MemoryInfo[] mi = aMgr.getProcessMemoryInfo(nPids);
+
+        sRet  = "Dalvik Private Dirty pages         " + mi[0].dalvikPrivateDirty     + " kb\n";
+        sRet += "Dalvik Proportional Set Size       " + mi[0].dalvikPss              + " kb\n";
+        sRet += "Dalvik Shared Dirty pages          " + mi[0].dalvikSharedDirty      + " kb\n\n";
+        sRet += "Native Private Dirty pages heap    " + mi[0].nativePrivateDirty     + " kb\n";
+        sRet += "Native Proportional Set Size heap  " + mi[0].nativePss              + " kb\n";
+        sRet += "Native Shared Dirty pages heap     " + mi[0].nativeSharedDirty      + " kb\n\n";
+        sRet += "Other Private Dirty pages          " + mi[0].otherPrivateDirty      + " kb\n";
+        sRet += "Other Proportional Set Size        " + mi[0].otherPss               + " kb\n";
+        sRet += "Other Shared Dirty pages           " + mi[0].otherSharedDirty       + " kb\n\n";
+        sRet += "Total Private Dirty Memory         " + mi[0].getTotalPrivateDirty() + " kb\n";
+        sRet += "Total Proportional Set Size Memory " + mi[0].getTotalPss()          + " kb\n";
+        sRet += "Total Shared Dirty Memory          " + mi[0].getTotalSharedDirty()  + " kb";
+
+
+        return(sRet);
+        }
+
+    public String GetTestRoot()
+        {
+        String    sRet = null;
+
+        if (Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED))
+            {
+            sRet = Environment.getExternalStorageDirectory().getAbsolutePath();
+            }
+        else
+            {
+            sRet = GetTmpDir();
+            }
+
+        return(sRet);
+        }
+
+    public String GetAppRoot(String AppName)
+        {
+        String sRet = sErrorPrefix + " internal error [no context]";
+        Context ctx = contextWrapper.getApplicationContext();
+
+        if (ctx != null)
+            {
+            try {
+                Context appCtx = ctx.createPackageContext(AppName, 0);
+                ContextWrapper appCtxW = new ContextWrapper(appCtx);
+                sRet = appCtxW.getPackageResourcePath();
+                appCtxW = null;
+                appCtx = null;
+                ctx = null;
+                System.gc();
+                }
+            catch (NameNotFoundException e)
+                {
+                e.printStackTrace();
+                }
+            }
+        return(sRet);
+        }
+
+    public String isDirectory(String sDir)
+        {
+        String    sRet = sErrorPrefix + sDir + " does not exist";
+        String    tmpDir    = fixFileName(sDir);
+        String [] theArgs = new String [3];
+        int    nFiles = 0;
+
+        if (tmpDir.contains("org.mozilla.fennec") || tmpDir.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = Uri.parse("content://" + (tmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir");
+
+            String[] columns = new String[] {
+                    "_id",
+                    "isdir",
+                    "filename",
+                    "length"
+                };
+
+            Cursor myCursor = cr.query(    ffxFiles,
+                                        columns,     // Which columns to return
+                                        tmpDir,     // Which rows to return (all rows)
+                                        null,       // Selection arguments (none)
+                                        null);        // Order clause (none)
+            if (myCursor != null) {
+                nFiles = myCursor.getCount();
+
+                // If no entries the dir is empty
+                if (nFiles > 0) {
+                    if  (myCursor.moveToPosition(0)) {
+                        sRet = ((myCursor.getLong(myCursor.getColumnIndex("isdir")) == 1) ? "TRUE" : "FALSE");
+                    }
+                }
+                myCursor.close();
+            }
+        } else {
+            File tmpFile = new java.io.File(tmpDir);
+
+            if (tmpFile.exists()) {
+                sRet = (tmpFile.isDirectory() ? "TRUE" : "FALSE");
+            }
+            else {
+                try {
+                    theArgs[0] = "su";
+                    theArgs[1] = "-c";
+                    theArgs[2] = "ls -l " + sDir;
+
+                    pProc = Runtime.getRuntime().exec(theArgs);
+                    RedirOutputThread outThrd = new RedirOutputThread(pProc, null);
+                    outThrd.start();
+                    outThrd.join(5000);
+                    sRet = outThrd.strOutput;
+                    if (!sRet.contains("No such file or directory") && sRet.startsWith("l"))
+                        sRet = "FALSE";
+                }
+                catch (IOException e) {
+                    sRet = e.getMessage();
+                    e.printStackTrace();
+                }
+                catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        return(sRet);
+        }
+
+
+    public String changeDir(String newDir)
+        {
+        String    tmpDir    = fixFileName(newDir);
+        String    sRet = sErrorPrefix + "Couldn't change directory to " + tmpDir;
+        int    nFiles = 0;
+
+        if (tmpDir.contains("org.mozilla.fennec") || tmpDir.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = Uri.parse("content://" + (tmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir");
+
+            String[] columns = new String[] {
+                    "_id",
+                    "isdir",
+                    "filename"
+                };
+
+            Cursor myCursor = cr.query(    ffxFiles,
+                                        columns,     // Which columns to return
+                                        tmpDir,     // Which rows to return (all rows)
+                                        null,       // Selection arguments (none)
+                                        null);        // Order clause (none)
+            if (myCursor != null) {
+                nFiles = myCursor.getCount();
+
+                if (nFiles > 0) {
+                    if  (myCursor.moveToPosition(0)) {
+                        if (myCursor.getLong(myCursor.getColumnIndex("isdir")) == 1) {
+                            currentDir = myCursor.getString(myCursor.getColumnIndex("filename"));
+                            sRet = "";
+                        }
+                    }
+                } else {
+                    sRet = sErrorPrefix + tmpDir + " is not a valid directory";
+                }
+                myCursor.close();
+            }
+        } else {
+            File tmpFile = new java.io.File(tmpDir);
+
+            if (tmpFile.exists()) {
+                try {
+                    if (tmpFile.isDirectory()) {
+                        currentDir = tmpFile.getCanonicalPath();
+                        sRet = "";
+                    }
+                else
+                    sRet = sErrorPrefix + tmpDir + " is not a valid directory";
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        return(sRet);
+        }
+
+    static final String HEXES = "0123456789abcdef";
+
+    public static String getHex( byte [] raw )
+        {
+        if ( raw == null )
+            {
+            return null;
+            }
+
+        final StringBuilder hex = new StringBuilder( 2 * raw.length );
+        for ( final byte b : raw )
+            {
+            hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
+            }
+        return hex.toString();
+        }
+
+    public String HashFile(String fileName)
+        {
+        String            sTmpFileName = fixFileName(fileName);
+        String            sRet         = sErrorPrefix + "Couldn't calculate hash for file " + sTmpFileName;
+        byte[]             buffer         = new byte [4096];
+        int                nRead         = 0;
+        long             lTotalRead     = 0;
+        MessageDigest    digest         = null;
+
+        try {
+            digest = java.security.MessageDigest.getInstance("MD5");
+            }
+        catch (NoSuchAlgorithmException e)
+            {
+            e.printStackTrace();
+            }
+
+        if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = null;
+
+            ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file");
+
+            String[] columns = new String[] {
+                    "_id",
+                    "chunk"
+                    };
+
+            Cursor myCursor = cr.query(    ffxFiles,
+                                        columns,         // Which columns to return
+                                        sTmpFileName,   // Which rows to return (all rows)
+                                        null,           // Selection arguments (none)
+                                        null);            // Order clause (none)
+            if (myCursor != null) {
+                int nRows = myCursor.getCount();
+                int nBytesRecvd = 0;
+
+                for (int lcv = 0; lcv < nRows; lcv++) {
+                    if  (myCursor.moveToPosition(lcv)) {
+                        byte [] buf = myCursor.getBlob(1);
+                        if (buf != null) {
+                            nBytesRecvd += buf.length;
+                            digest.update(buf, 0, buf.length);
+                            lTotalRead += nRead;
+                            buf = null;
+                        }
+                    }
+                }
+                myCursor.close();
+                byte [] hash = digest.digest();
+
+                sRet = getHex(hash);
+            }
+        } else {
+            try {
+                FileInputStream srcFile  = new FileInputStream(sTmpFileName);
+                while((nRead = srcFile.read(buffer)) != -1) {
+                    digest.update(buffer, 0, nRead);
+                    lTotalRead += nRead;
+                }
+                srcFile.close();
+                byte [] hash = digest.digest();
+
+                sRet = getHex(hash);
+            }
+            catch (FileNotFoundException e) {
+                sRet += " file not found";
+                e.printStackTrace();
+            }
+            catch (IOException e) {
+                sRet += " io exception";
+                e.printStackTrace();
+            }
+        }
+        return(sRet);
+    }
+
+    public String RemoveFile(String fileName)
+        {
+        String    sTmpFileName = fixFileName(fileName);
+        String    sRet = sErrorPrefix + "Couldn't delete file " + sTmpFileName;
+
+        if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file");
+            if (cr.delete(ffxFiles, sTmpFileName, null) == 1) {
+                sRet = "deleted " + sTmpFileName;
+            }
+        } else {
+            File f = new File(sTmpFileName);
+
+            if (f.delete())
+                sRet = "deleted " + sTmpFileName;
+        }
+
+        return(sRet);
+        }
+
+    public String PruneDir(String sDir)
+        {
+        String    sRet = "";
+        int nFiles = 0;
+        String sSubDir = null;
+        String    sTmpDir = fixFileName(sDir);
+
+        if (sTmpDir.contains("org.mozilla.fennec") || sTmpDir.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = Uri.parse("content://" + (sTmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir");
+            if (cr.delete(ffxFiles, sTmpDir, null) > 0) {
+                sRet = "deleted " + sTmpDir;
+            }
+        } else {
+            File dir = new File(sTmpDir);
+
+            if (dir.isDirectory()) {
+                sRet = "Deleting file(s) from " + sTmpDir;
+
+                File [] files = dir.listFiles();
+                if (files != null) {
+                    if ((nFiles = files.length) > 0) {
+                        for (int lcv = 0; lcv < nFiles; lcv++) {
+                            if (files[lcv].isDirectory()) {
+                                sSubDir = files[lcv].getAbsolutePath();
+                                sRet += "\n" + PruneDir(sSubDir);
+                            }
+                            else {
+                                if (files[lcv].delete()) {
+                                sRet += "\n\tDeleted " + files[lcv].getName();
+                                }
+                                else {
+                                    sRet += "\n\tUnable to delete " + files[lcv].getName();
+                                }
+                            }
+                        }
+                    }
+                    else
+                        sRet += "\n\t<empty>";
+                }
+
+                if (dir.delete()) {
+                    sRet += "\nDeleting directory " + sTmpDir;
+                }
+                else {
+                    sRet += "\nUnable to delete directory " + sTmpDir;
+                }
+            }
+            else {
+                sRet += sErrorPrefix + sTmpDir + " is not a directory";
+            }
+        }
+
+        return(sRet);
+        }
+
+    public String PrintDir(String sDir)
+        {
+        String    sRet = "";
+        int nFiles = 0;
+        String    sTmpDir = fixFileName(sDir);
+
+        if (sTmpDir.contains("org.mozilla.fennec") || sTmpDir.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = null;
+
+            ffxFiles = Uri.parse("content://" + (sTmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir");
+
+            String[] columns = new String[] {
+                    "_id",
+                    "isdir",
+                    "filename",
+                    "length"
+                };
+
+            Cursor myCursor = cr.query(    ffxFiles,
+                                        columns,     // Which columns to return
+                                        sTmpDir,    // Which rows to return (all rows)
+                                        null,       // Selection arguments (none)
+                                        null);        // Order clause (none)
+            if (myCursor != null) {
+                nFiles = myCursor.getCount();
+
+                // If only one entry and the index is -1 this is not a directory
+                int nNdx = myCursor.getColumnIndex("_id");
+                // If no entries the dir is empty
+                if (nFiles == 1) {
+                    sRet = "<empty>";
+                } else {
+                    // Show the entries
+                    for (int lcv = 1; lcv < nFiles; lcv++) {
+                        if  (myCursor.moveToPosition(lcv)) {
+                            if ((lcv == 0) && (myCursor.getLong(nNdx) == -1)) {
+                                sRet = sErrorPrefix + sTmpDir + " is not a directory";
+                            } else {
+                                sRet += myCursor.getString(2);
+                                if (lcv < (nFiles - 1))
+                                    sRet += "\n";
+                            }
+                        }
+                    }
+                }
+                myCursor.close();
+            }
+        } else {
+            File dir = new File(sTmpDir);
+
+            if (dir.isDirectory()) {
+                File [] files = dir.listFiles();
+
+                if (files != null) {
+                    if ((nFiles = files.length) > 0) {
+                        for (int lcv = 0; lcv < nFiles; lcv++) {
+                            sRet += files[lcv].getName();
+                            if (lcv < (nFiles - 1)) {
+                                sRet += "\n";
+                            }
+                        }
+                    }
+                    else {
+                        sRet = "<empty>";
+                    }
+                }
+            }
+            else {
+                sRet = sErrorPrefix + sTmpDir + " is not a directory";
+            }
+        }
+        return(sRet);
+    }
+
+    public String Move(String sTmpSrcFileName, String sTmpDstFileName) {
+        String sRet = sErrorPrefix + "Could not move " + sTmpSrcFileName + " to " + sTmpDstFileName;
+        String sTmp = CopyFile(sTmpSrcFileName, sTmpDstFileName);
+        if (sTmp.contains(" copied to ")) {
+            sTmp = RemoveFile(sTmpSrcFileName);
+            if (sTmp.startsWith("deleted ")) {
+                sRet = sTmpSrcFileName + " moved to " + sTmpDstFileName;
+            }
+        }
+
+        return(sRet);
+    }
+
+    public String CopyFile(String sTmpSrcFileName, String sTmpDstFileName) {
+        String sRet = sErrorPrefix + "Could not copy " + sTmpSrcFileName + " to " + sTmpDstFileName;
+        ContentValues cv = null;
+        File destFile = null;
+        Uri ffxSrcFiles = null;
+        Uri ffxDstFiles = null;
+        FileInputStream srcFile  = null;
+        FileOutputStream dstFile  = null;
+        byte[] buffer = new byte [4096];
+        int    nRead = 0;
+        long lTotalRead = 0;
+        long lTotalWritten = 0;
+        ContentResolver crIn = null;
+        ContentResolver crOut = null;
+
+        if (sTmpSrcFileName.contains("org.mozilla.fennec") || sTmpSrcFileName.contains("org.mozilla.firefox")) {
+            ffxSrcFiles = Uri.parse("content://" + (sTmpSrcFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file");
+            crIn = contextWrapper.getContentResolver();
+        } else {
+            try {
+                srcFile  = new FileInputStream(sTmpSrcFileName);
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (sTmpDstFileName.contains("org.mozilla.fennec") || sTmpDstFileName.contains("org.mozilla.firefox")) {
+            ffxDstFiles = Uri.parse("content://" + (sTmpDstFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file");
+            crOut = contextWrapper.getContentResolver();
+            cv = new ContentValues();
+        } else {
+            try {
+                dstFile  = new FileOutputStream(sTmpDstFileName);
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (srcFile != null) {
+            try {
+                while((nRead = srcFile.read(buffer)) != -1)    {
+                    lTotalRead += nRead;
+                    if (dstFile != null) {
+                        dstFile.write(buffer, 0, nRead);
+                        dstFile.flush();
+                    } else {
+                        cv.put("length", nRead);
+                        cv.put("chunk", buffer);
+                        if (crOut.update(ffxDstFiles, cv, sTmpDstFileName, null) == 0)
+                            break;
+                        lTotalWritten += nRead;
+                    }
+                }
+
+                srcFile.close();
+
+                if (dstFile != null) {
+                    dstFile.flush();
+                    dstFile.close();
+
+                    destFile = new File(sTmpDstFileName);
+                    lTotalWritten = destFile.length();
+                }
+
+                if (lTotalWritten == lTotalRead) {
+                    sRet = sTmpSrcFileName + " copied to " + sTmpDstFileName;
+                }
+                else {
+                    sRet = sErrorPrefix + "Failed to copy " + sTmpSrcFileName + " [length = " + lTotalWritten + "] to " + sTmpDstFileName + " [length = " + lTotalRead + "]";
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+        } else {
+            String[] columns = new String[] {
+                    "_id",
+                    "chunk",
+                    "length"
+                    };
+
+            Cursor myCursor = crIn.query(ffxSrcFiles,
+                                        columns,             // Which columns to return
+                                        sTmpSrcFileName,       // Which rows to return (all rows)
+                                        null,               // Selection arguments (none)
+                                        null);                // Order clause (none)
+            if (myCursor != null) {
+                int nRows = myCursor.getCount();
+
+                byte [] buf = null;
+
+                for (int lcv = 0; lcv < nRows; lcv++) {
+                    if  (myCursor.moveToPosition(lcv)) {
+                        buf = myCursor.getBlob(myCursor.getColumnIndex("chunk"));
+                        if (buf != null) {
+                            nRead = buf.length;
+                            try {
+                                lTotalRead += nRead;
+                                if (dstFile != null) {
+                                    dstFile.write(buffer, 0, nRead);
+                                    dstFile.flush();
+                                } else {
+                                    cv.put("length", nRead);
+                                    cv.put("chunk", buffer);
+                                    if (crOut.update(ffxDstFiles, cv, sTmpDstFileName, null) == 0)
+                                        break;
+                                    lTotalWritten += nRead;
+                                }
+                            } catch (IOException e) {
+                                e.printStackTrace();
+                            }
+                            buf = null;
+                        }
+                    }
+                }
+
+                if (nRows == -1) {
+                    sRet = sErrorPrefix + sTmpSrcFileName + ",-1\nNo such file or directory";
+                }
+                else {
+                    myCursor.close();
+
+                    if (dstFile != null) {
+                        try {
+                            dstFile.flush();
+                            dstFile.close();
+
+                            destFile = new File(sTmpDstFileName);
+                            lTotalWritten = destFile.length();
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+
+                    if (lTotalWritten == lTotalRead) {
+                        sRet = sTmpSrcFileName + " copied to " + sTmpDstFileName;
+                    }
+                    else {
+                        sRet = sErrorPrefix + "Failed to copy " + sTmpSrcFileName + " [length = " + lTotalWritten + "] to " + sTmpDstFileName + " [length = " + lTotalRead + "]";
+                    }
+                }
+            }
+            else {
+                sRet = sErrorPrefix + sTmpSrcFileName + ",-1\nUnable to access file (internal error)";
+            }
+        }
+
+        return (sRet);
+    }
+
+    public String IsDirWritable(String sDir)
+        {
+        String    sTmpDir = fixFileName(sDir);
+        String sRet = sErrorPrefix + "[" + sTmpDir + "] is not a directory";
+
+        if (sTmpDir.contains("org.mozilla.fennec") || sTmpDir.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = null;
+
+            ffxFiles = Uri.parse("content://" + (sTmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir");
+
+            String[] columns = new String[] {
+                    "_id",
+                    "isdir",
+                    "filename",
+                    "length",
+                    "writable"
+                };
+
+            Cursor myCursor = cr.query(    ffxFiles,
+                                        columns,     // Which columns to return
+                                        sTmpDir,    // Which rows to return (all rows)
+                                        null,       // Selection arguments (none)
+                                        null);        // Order clause (none)
+            if (myCursor != null) {
+                if (myCursor.getCount() > 0) {
+                    if (myCursor.moveToPosition(0)) {
+                        if (myCursor.getLong(myCursor.getColumnIndex("isdir")) == 1) {
+                            sRet = "[" + sTmpDir + "] " + ((myCursor.getLong(myCursor.getColumnIndex("writable")) == 1) ? "is" : "is not") + " writable";
+                        }
+                    }
+                }
+            }
+        } else {
+            File dir = new File(sTmpDir);
+
+            if (dir.isDirectory()) {
+                sRet = "[" + sTmpDir + "] " + (dir.canWrite() ? "is" : "is not") + " writable";
+            } else {
+                sRet = sErrorPrefix + "[" + sTmpDir + "] is not a directory";
+            }
+        }
+        return(sRet);
+    }
+
+    public String Push(String fileName, BufferedInputStream bufIn, long lSize)
+    {
+        byte []                buffer             = new byte [8192];
+        int                    nRead            = 0;
+        long                lRead            = 0;
+        String                sTmpFileName     = fixFileName(fileName);
+        FileOutputStream     dstFile            = null;
+        ContentResolver     cr                 = null;
+        ContentValues        cv                = null;
+        Uri                 ffxFiles         = null;
+        String                sRet            = sErrorPrefix + "Push failed!";
+
+        try {
+            if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) {
+                cr = contextWrapper.getContentResolver();
+                ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file");
+                cv = new ContentValues();
+            }
+            else {
+                dstFile = new FileOutputStream(sTmpFileName, false);
+            }
+
+            while((nRead != -1) && (lRead < lSize))
+                {
+                nRead = bufIn.read(buffer);
+                if (nRead != -1) {
+                    if (dstFile != null) {
+                        dstFile.write(buffer, 0, nRead);
+                        dstFile.flush();
+                    }
+                    else {
+                        cv.put("offset", lRead);
+                        cv.put("length", nRead);
+                        cv.put("chunk", buffer);
+                        cr.update(ffxFiles, cv, sTmpFileName, null);
+                    }
+                    lRead += nRead;
+                }
+            }
+
+            if (dstFile != null) {
+                dstFile.flush();
+                dstFile.close();
+            }
+
+            if (lRead == lSize)    {
+                sRet = HashFile(sTmpFileName);
+            }
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        buffer = null;
+
+        return(sRet);
+    }
+
+    public String FTPGetFile(String sServer, String sSrcFileName, String sDstFileName, OutputStream out)
+        {
+        byte[] buffer = new byte [4096];
+        int    nRead = 0;
+        long lTotalRead = 0;
+        String sRet = sErrorPrefix + "FTP Get failed for " + sSrcFileName;
+        String strRet = "";
+        int    reply = 0;
+        FileOutputStream outStream = null;
+        String    sTmpDstFileName = fixFileName(sDstFileName);
+
+        FTPClient ftp = new FTPClient();
+        try
+            {
+            ftp.connect(sServer);
+            reply = ftp.getReplyCode();
+            if(FTPReply.isPositiveCompletion(reply))
+                {
+                ftp.login("anonymous", "b@t.com");
+                reply = ftp.getReplyCode();
+                if(FTPReply.isPositiveCompletion(reply))
+                    {
+                    ftp.enterLocalPassiveMode();
+                    if (ftp.setFileType(FTP.BINARY_FILE_TYPE))
+                        {
+                        File dstFile = new File(sTmpDstFileName);
+                        outStream = new FileOutputStream(dstFile);
+                        FTPFile [] ftpFiles = ftp.listFiles(sSrcFileName);
+                        if (ftpFiles.length > 0)
+                            {
+                            long lFtpSize = ftpFiles[0].getSize();
+                            if (lFtpSize <= 0)
+                                lFtpSize = 1;
+
+                            InputStream ftpIn = ftp.retrieveFileStream(sSrcFileName);
+                            while ((nRead = ftpIn.read(buffer)) != -1)
+                                {
+                                lTotalRead += nRead;
+                                outStream.write(buffer, 0, nRead);
+                                strRet = "\r" + lTotalRead + " of " + lFtpSize + " bytes received " + ((lTotalRead * 100) / lFtpSize) + "% completed";
+                                out.write(strRet.getBytes());
+                                out.flush();
+                                }
+                            ftpIn.close();
+                            @SuppressWarnings("unused")
+                            boolean bRet = ftp.completePendingCommand();
+                            outStream.flush();
+                            outStream.close();
+                            strRet = ftp.getReplyString();
+                            reply = ftp.getReplyCode();
+                            }
+                        else
+                            {
+                            strRet = sRet;
+                            }
+                        }
+                    ftp.logout();
+                    ftp.disconnect();
+                    sRet = "\n" + strRet;
+                    }
+                else
+                    {
+                    ftp.disconnect();
+                    System.err.println("FTP server refused login.");
+                    }
+                }
+            else
+                {
+                ftp.disconnect();
+                System.err.println("FTP server refused connection.");
+                }
+            }
+        catch (SocketException e)
+            {
+            sRet = e.getMessage();
+            strRet = ftp.getReplyString();
+            reply = ftp.getReplyCode();
+            sRet += "\n" + strRet;
+            e.printStackTrace();
+            }
+        catch (IOException e)
+            {
+            sRet = e.getMessage();
+            strRet = ftp.getReplyString();
+            reply = ftp.getReplyCode();
+            sRet += "\n" + strRet;
+            e.printStackTrace();
+            }
+        return (sRet);
+    }
+
+    public String Pull(String fileName, long lOffset, long lLength, OutputStream out)
+        {
+        String    sTmpFileName = fixFileName(fileName);
+        String    sRet = sErrorPrefix + "Could not read the file " + sTmpFileName;
+        byte[]    buffer = new byte [4096];
+        int        nRead = 0;
+        long    lSent = 0;
+
+        if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = null;
+
+            ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file");
+
+            String[] columns = new String[] {
+                    "_id",
+                    "chunk",
+                    "length"
+                    };
+
+            String [] args = new String [2];
+            args[0] = Long.toString(lOffset);
+            args[1] = Long.toString(lLength);
+
+            Cursor myCursor = cr.query(    ffxFiles,
+                                        columns,         // Which columns to return
+                                        sTmpFileName,   // Which rows to return (all rows)
+                                        args,           // Selection arguments (none)
+                                        null);            // Order clause (none)
+            if (myCursor != null) {
+                int nRows = myCursor.getCount();
+                long lFileLength = 0;
+
+                for (int lcv = 0; lcv < nRows; lcv++) {
+                    if  (myCursor.moveToPosition(lcv)) {
+                        if (lcv == 0) {
+                            lFileLength = myCursor.getLong(2);
+                            String sTmp = sTmpFileName + "," + lFileLength + "\n";
+                            try {
+                                out.write(sTmp.getBytes());
+                            } catch (IOException e) {
+                                e.printStackTrace();
+                                break;
+                            }
+                        }
+
+                        if (lLength != 0) {
+                            byte [] buf = myCursor.getBlob(1);
+                            if (buf != null) {
+                                nRead = buf.length;
+                                try {
+                                    if ((lSent + nRead) <= lFileLength)    {
+                                        out.write(buf,0,nRead);
+                                        lSent += nRead;
+                                    }
+                                    else {
+                                        nRead = (int) (lFileLength - lSent);
+                                        out.write(buf,0,nRead);
+                                        Log.d("pull warning", "more bytes read than expected");
+                                        break;
+                                    }
+                                } catch (IOException e) {
+                                    e.printStackTrace();
+                                    sRet = sErrorPrefix + "Could not write to out " + sTmpFileName;
+                                }
+                                buf = null;
+                            }
+                        }
+                    }
+                }
+                if (nRows == 0) {
+                    String sTmp = sTmpFileName + "," + lFileLength + "\n";
+                    try {
+                        out.write(sTmp.getBytes());
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+                if (nRows == -1) {
+                    sRet = sErrorPrefix + sTmpFileName + ",-1\nNo such file or directory";
+                }
+                else {
+                    myCursor.close();
+                    sRet = "";
+                }
+            }
+            else {
+                sRet = sErrorPrefix + sTmpFileName + ",-1\nUnable to access file (internal error)";
+            }
+        }
+        else {
+            try {
+                File f = new File(sTmpFileName);
+                long lFileLength = f.length();
+                FileInputStream fin = new FileInputStream(f);
+                if (lFileLength == 0) {
+                    while ((nRead = fin.read(buffer)) != -1) {
+                        lFileLength += nRead;
+                    }
+                    fin.close();
+                    fin = new FileInputStream(f);
+                }
+
+                // lLength == -1 return everything between lOffset and eof
+                // lLength == 0 return file length
+                // lLength > 0 return lLength bytes
+                if (lLength == -1) {
+                    lFileLength = lFileLength - lOffset;
+                } else if (lLength == 0) {
+                    // just return the file length
+                } else {
+                    lFileLength = ((lLength <= (lFileLength - lOffset)) ? lLength : (lFileLength - lOffset));
+                }
+
+                String sTmp = sTmpFileName + "," + lFileLength + "\n";
+                out.write(sTmp.getBytes());
+                if (lLength != 0) {
+                    if  (lOffset > 0) {
+                        fin.skip(lOffset);
+                    }
+                    while ((nRead = fin.read(buffer)) != -1) {
+                        if ((lSent + nRead) <= lFileLength)    {
+                            out.write(buffer,0,nRead);
+                            lSent += nRead;
+                        }
+                        else {
+                            nRead = (int) (lFileLength - lSent);
+                            out.write(buffer,0,nRead);
+                            if (lLength != -1)
+                                Log.d("pull warning", "more bytes read than sent");
+                            break;
+                        }
+                    }
+                }
+                fin.close();
+                out.flush();
+                sRet = "";
+                }
+            catch (FileNotFoundException e)    {
+                sRet = sErrorPrefix + sTmpFileName + ",-1\nNo such file or directory";
+            }
+            catch (IOException e) {
+                sRet = e.toString();
+            }
+        }
+        return (sRet);
+    }
+
+    public String Cat(String fileName, OutputStream out)
+        {
+        String    sTmpFileName = fixFileName(fileName);
+        String    sRet = sErrorPrefix + "Could not read the file " + sTmpFileName;
+        byte[]    buffer = new byte [4096];
+        int        nRead = 0;
+
+        if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = null;
+
+            ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/file");
+
+            String[] columns = new String[] {
+                    "_id",
+                    "chunk"
+                    };
+
+            Cursor myCursor = cr.query(    ffxFiles,
+                                        columns,         // Which columns to return
+                                        sTmpFileName,   // Which rows to return (all rows)
+                                        null,           // Selection arguments (none)
+                                        null);            // Order clause (none)
+            if (myCursor != null) {
+                int nRows = myCursor.getCount();
+                int nBytesRecvd = 0;
+
+                for (int lcv = 0; lcv < nRows; lcv++) {
+                    if  (myCursor.moveToPosition(lcv)) {
+                        byte [] buf = myCursor.getBlob(1);
+                        if (buf != null) {
+                            nBytesRecvd += buf.length;
+                            try {
+                                out.write(buf);
+                                sRet = "";
+                            } catch (IOException e) {
+                                e.printStackTrace();
+                                sRet = sErrorPrefix + "Could not write to out " + sTmpFileName;
+                            }
+                            buf = null;
+                        }
+                    }
+                }
+                if (nRows == 0) {
+                    sRet = "";
+                }
+
+                myCursor.close();
+            }
+        } else {
+            try {
+                FileInputStream fin = new FileInputStream(sTmpFileName);
+                while ((nRead = fin.read(buffer)) != -1) {
+                    out.write(buffer,0,nRead);
+                }
+                fin.close();
+                out.flush();
+                sRet = "";
+                }
+            catch (FileNotFoundException e) {
+                sRet = sErrorPrefix + sTmpFileName + " No such file or directory";
+            }
+            catch (IOException e) {
+                sRet = e.toString();
+            }
+        }
+        return (sRet);
+    }
+
+    public String MakeDir(String sDir)
+        {
+        String    sTmpDir = fixFileName(sDir);
+        String sRet = sErrorPrefix + "Could not create the directory " + sTmpDir;
+        if (sTmpDir.contains("org.mozilla.fennec") || sTmpDir.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = Uri.parse("content://" + (sTmpDir.contains("fennec") ? fenProvider : ffxProvider) + "/dir");
+            ContentValues cv = new ContentValues();
+
+            if (cr.update(ffxFiles, cv, sTmpDir, null) == 1) {
+                sRet = sDir + " successfully created";
+            }
+        }
+        else {
+            File dir = new File(sTmpDir);
+
+            if (dir.mkdirs())
+                sRet = sDir + " successfully created";
+        }
+
+        return (sRet);
+        }
+    // move this to SUTAgentAndroid.java
+    public String GetScreenInfo()
+        {
+        String sRet = "";
+        DisplayMetrics metrics = new DisplayMetrics();
+        WindowManager wMgr = (WindowManager) contextWrapper.getSystemService(Context.WINDOW_SERVICE);
+        wMgr.getDefaultDisplay().getMetrics(metrics);
+        sRet = "X:" + metrics.widthPixels + " Y:" + metrics.heightPixels;
+        return (sRet);
+        }
+    // move this to SUTAgentAndroid.java
+    public int [] GetScreenXY()
+        {
+            int [] nRetXY = new int [2];
+            DisplayMetrics metrics = new DisplayMetrics();
+            WindowManager wMgr = (WindowManager) contextWrapper.getSystemService(Context.WINDOW_SERVICE);
+            wMgr.getDefaultDisplay().getMetrics(metrics);
+            nRetXY[0] = metrics.widthPixels;
+            nRetXY[1] = metrics.heightPixels;
+            return(nRetXY);
+        }
+
+    public String SetADB(String sWhat) {
+        String sRet = "";
+        String sTmp = "";
+        String [] theArgs = new String [3];
+
+        theArgs[0] = "su";
+        theArgs[1] = "-c";
+
+        if (sWhat.contains("ip")) {
+            theArgs[2] = "setprop service.adb.tcp.port 5555";
+        } else {
+            theArgs[2] = "setprop service.adb.tcp.port -1";
+        }
+
+        try {
+            pProc = Runtime.getRuntime().exec(theArgs);
+            RedirOutputThread outThrd = new RedirOutputThread(pProc, null);
+            outThrd.start();
+            outThrd.join(5000);
+            sTmp = outThrd.strOutput;
+            Log.e("ADB", sTmp);
+            if (outThrd.nExitCode == 0) {
+                theArgs[2] = "stop adbd";
+                pProc = Runtime.getRuntime().exec(theArgs);
+                outThrd = new RedirOutputThread(pProc, null);
+                outThrd.start();
+                outThrd.join(5000);
+                sTmp = outThrd.strOutput;
+                Log.e("ADB", sTmp);
+                if (outThrd.nExitCode == 0) {
+                    theArgs[2] = "start adbd";
+                    pProc = Runtime.getRuntime().exec(theArgs);
+                    outThrd = new RedirOutputThread(pProc, null);
+                    outThrd.start();
+                    outThrd.join(5000);
+                    sTmp = outThrd.strOutput;
+                    Log.e("ADB", sTmp);
+                    if (outThrd.nExitCode == 0) {
+                        sRet = "Successfully set adb to " + sWhat + "\n";
+                    } else {
+                        sRet = sErrorPrefix + "Failed to start adbd\n";
+                    }
+                } else {
+                    sRet = sErrorPrefix + "Failed to stop adbd\n";
+                }
+            } else {
+                sRet = sErrorPrefix + "Failed to setprop service.adb.tcp.port 5555\n";
+            }
+
+        }
+    catch (IOException e)
+        {
+        sRet = e.getMessage();
+        e.printStackTrace();
+        }
+    catch (InterruptedException e)
+        {
+        e.printStackTrace();
+        }
+        return(sRet);
+    }
+
+    public String KillProcess(String sProcName, OutputStream out)
+        {
+        String sTmp = "";
+        String [] theArgs = new String [3];
+
+        theArgs[0] = "su";
+        theArgs[1] = "-c";
+        theArgs[2] = "kill";
+
+        String sRet = sErrorPrefix + "Unable to kill " + sProcName + "\n";
+        ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
+        List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses();
+        int lcv = 0;
+        String strProcName = "";
+        int    nPID = 0;
+
+        for (lcv = 0; lcv < lProcesses.size(); lcv++)
+            {
+            if (lProcesses.get(lcv).processName.contains(sProcName))
+                {
+                strProcName = lProcesses.get(lcv).processName;
+                nPID = lProcesses.get(lcv).pid;
+                sRet = sErrorPrefix + "Failed to kill " + nPID + " " + strProcName + "\n";
+
+                theArgs[2] += " " + nPID;
+
+                try
+                    {
+                    pProc = Runtime.getRuntime().exec(theArgs);
+                    RedirOutputThread outThrd = new RedirOutputThread(pProc, null);
+                    outThrd.start();
+                    outThrd.join(15000);
+                    sTmp = outThrd.strOutput;
+                    Log.e("KILLPROCESS", sTmp);
+                    if (outThrd.nExitCode == 0) {
+                        sRet = "Successfully killed " + nPID + " " + strProcName + "\n";
+                        nPID = 0;
+                        break;
+                    } else {
+                        sRet = sErrorPrefix + "Failed to kill " + nPID + " " + strProcName + "\n";
+                    }
+                    }
+                catch (IOException e)
+                    {
+                    sRet = e.getMessage();
+                    e.printStackTrace();
+                    }
+                catch (InterruptedException e)
+                    {
+                    e.printStackTrace();
+                    }
+
+                // Give the messages a chance to be processed
+                try {
+                    Thread.sleep(2000);
+                    }
+                catch (InterruptedException e)
+                    {
+                    e.printStackTrace();
+                    }
+                break;
+                }
+            }
+
+        if (nPID > 0)
+            {
+            sRet = "Successfully killed " + nPID + " " + strProcName + "\n";
+            lProcesses = aMgr.getRunningAppProcesses();
+            for (lcv = 0; lcv < lProcesses.size(); lcv++)
+                {
+                if (lProcesses.get(lcv).processName.contains(sProcName))
+                    {
+                    sRet = sErrorPrefix + "Unable to kill " + nPID + " " + strProcName + "\n";
+                    break;
+                    }
+                }
+            }
+
+        return (sRet);
+        }
+
+    public boolean IsProcessDead(String sProcName)
+        {
+        boolean bRet = false;
+        ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
+        List <ActivityManager.ProcessErrorStateInfo> lProcesses = aMgr.getProcessesInErrorState();
+        int lcv = 0;
+
+        if (lProcesses != null)
+            {
+            for (lcv = 0; lcv < lProcesses.size(); lcv++)
+                {
+                if (lProcesses.get(lcv).processName.contentEquals(sProcName) &&
+                    lProcesses.get(lcv).condition != ActivityManager.ProcessErrorStateInfo.NO_ERROR)
+                    {
+                    bRet = true;
+                    break;
+                    }
+                }
+            }
+
+        return (bRet);
+        }
+
+    public String GetProcessInfo()
+        {
+        String sRet = "";
+        ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
+        List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses();
+        int    nProcs = lProcesses.size();
+        int lcv = 0;
+        String strProcName = "";
+        int    nPID = 0;
+        int nUser = 0;
+
+        for (lcv = 0; lcv < nProcs; lcv++)
+            {
+            strProcName = lProcesses.get(lcv).processName;
+            nPID = lProcesses.get(lcv).pid;
+            nUser = lProcesses.get(lcv).uid;
+            sRet += nUser + "\t" + nPID + "\t" + strProcName;
+            if (lcv < (nProcs - 1))
+                sRet += "\n";
+            }
+
+        return (sRet);
+        }
+
+    public String GetOSInfo()
+        {
+        String sRet = "";
+
+        sRet = Build.DISPLAY;
+
+        return (sRet);
+        }
+
+    public String GetPowerInfo()
+        {
+        String sRet = "";
+
+        sRet = "Power status:\n  AC power " + SUTAgentAndroid.sACStatus + "\n";
+        sRet += "  Battery charge " + SUTAgentAndroid.sPowerStatus + "\n";
+        sRet += "  Remaining charge:      " + SUTAgentAndroid.nChargeLevel + "%\n";
+        sRet += "  Battery Temperature:   " + (((float)(SUTAgentAndroid.nBatteryTemp))/10) + " (c)\n";
+        return (sRet);
+        }
+
+    // todo
+    public String GetDiskInfo(String sPath)
+        {
+        String sRet = "";
+        StatFs statFS = new StatFs(sPath);
+
+        int nBlockCount = statFS.getBlockCount();
+        int nBlockSize = statFS.getBlockSize();
+        int nBlocksAvail = statFS.getAvailableBlocks();
+        int nBlocksFree = statFS.getFreeBlocks();
+
+        sRet = "total:     " + (nBlockCount * nBlockSize) + "\nfree:      " + (nBlocksFree * nBlockSize) + "\navailable: " + (nBlocksAvail * nBlockSize);
+
+        return (sRet);
+        }
+
+    public String GetMemoryInfo()
+        {
+        String sRet = "PA:" + GetMemoryConfig();
+        return (sRet);
+        }
+
+    public long GetMemoryConfig()
+        {
+        ActivityManager aMgr = (ActivityManager) contextWrapper.getSystemService(Activity.ACTIVITY_SERVICE);
+        ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
+        aMgr.getMemoryInfo(outInfo);
+        long lMem = outInfo.availMem;
+
+        return (lMem);
+        }
+
+    public String UpdateCallBack(String sFileName)
+        {
+        String sRet = sErrorPrefix + "No file specified";
+        String sIP = "";
+        String sPort = "";
+        int nEnd = 0;
+        int nStart = 0;
+
+        if ((sFileName == null) || (sFileName.length() == 0))
+            return(sRet);
+
+        Context ctx = contextWrapper.getApplicationContext();
+        try {
+            FileInputStream fis = ctx.openFileInput(sFileName);
+            int nBytes = fis.available();
+            if (nBytes > 0)
+                {
+                byte [] buffer = new byte [nBytes + 1];
+                int nRead = fis.read(buffer, 0, nBytes);
+                fis.close();
+                ctx.deleteFile(sFileName);
+                if (nRead > 0)
+                    {
+                    String sBuffer = new String(buffer);
+                    nEnd = sBuffer.indexOf(',');
+                    if (nEnd > 0)
+                        {
+                        sIP = (sBuffer.substring(0, nEnd)).trim();
+                        nStart = nEnd + 1;
+                        nEnd = sBuffer.indexOf('\r', nStart);
+                        if (nEnd > 0)
+                            {
+                            sPort = (sBuffer.substring(nStart, nEnd)).trim();
+                            Thread.sleep(5000);
+                            sRet = RegisterTheDevice(sIP, sPort, sBuffer.substring(nEnd + 1));
+                            }
+                        }
+                    }
+                }
+            }
+        catch (FileNotFoundException e)
+            {
+            sRet = sErrorPrefix + "Nothing to do";
+            }
+        catch (IOException e)
+            {
+            sRet = sErrorPrefix + "Couldn't send info to " + sIP + ":" + sPort;
+            }
+        catch (InterruptedException e)
+            {
+            e.printStackTrace();
+            }
+        return(sRet);
+        }
+
+    public String RegisterTheDevice(String sSrvr, String sPort, String sData)
+        {
+        String sRet = "";
+        String line = "";
+
+//        Debug.waitForDebugger();
+
+        if (sSrvr != null && sPort != null && sData != null)
+            {
+            try
+                {
+                int nPort = Integer.parseInt(sPort);
+                Socket socket = new Socket(sSrvr, nPort);
+                PrintWriter out = new PrintWriter(socket.getOutputStream(), false);
+                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+                out.println(sData);
+                if ( out.checkError() == false )
+                    {
+                    socket.setSoTimeout(30000);
+                    while (socket.isInputShutdown() == false)
+                        {
+                        line = in.readLine();
+
+                        if (line != null)
+                            {
+                            line = line.toLowerCase();
+                            sRet += line;
+                            // ok means we're done
+                            if (line.contains("ok"))
+                                break;
+                            }
+                        else
+                            {
+                            // end of stream reached
+                            break;
+                            }
+                        }
+                    }
+                out.close();
+                in.close();
+                socket.close();
+                }
+            catch(NumberFormatException e)
+                {
+                sRet += "reg NumberFormatException thrown [" + e.getLocalizedMessage() + "]";
+                e.printStackTrace();
+                }
+            catch (UnknownHostException e)
+                {
+                sRet += "reg UnknownHostException thrown [" + e.getLocalizedMessage() + "]";
+                e.printStackTrace();
+                }
+            catch (IOException e)
+                {
+                sRet += "reg IOException thrown [" + e.getLocalizedMessage() + "]";
+                e.printStackTrace();
+                }
+            }
+        return(sRet);
+        }
+
+    public String GetInternetData(String sHost, String sPort, String sURL)
+        {
+        String sRet = "";
+        String sNewURL = "";
+        HttpClient httpClient = new DefaultHttpClient();
+        try
+            {
+            sNewURL = "http://" + sHost + ((sPort.length() > 0) ? (":" + sPort) : "") + sURL;
+
+            HttpGet request = new HttpGet(sNewURL);
+            HttpResponse response = httpClient.execute(request);
+            int status = response.getStatusLine().getStatusCode();
+            // we assume that the response body contains the error message
+            if (status != HttpStatus.SC_OK)
+                {
+                ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+                response.getEntity().writeTo(ostream);
+                Log.e("HTTP CLIENT", ostream.toString());
+                }
+            else
+                {
+                InputStream content = response.getEntity().getContent();
+                byte [] data = new byte [2048];
+                int nRead = content.read(data);
+                sRet = new String(data, 0, nRead);
+                content.close(); // this will also close the connection
+                }
+            }
+        catch (IllegalArgumentException e)
+            {
+            sRet = e.getLocalizedMessage();
+            e.printStackTrace();
+            }
+        catch (ClientProtocolException e)
+            {
+            sRet = e.getLocalizedMessage();
+            e.printStackTrace();
+            }
+        catch (IOException e)
+            {
+            sRet = e.getLocalizedMessage();
+            e.printStackTrace();
+            }
+
+        return(sRet);
+        }
+
+    public String GetTimeZone()
+        {
+        String    sRet = "";
+        TimeZone tz;
+
+        tz = TimeZone.getDefault();
+        Date now = new Date();
+        sRet = tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG);
+
+        return(sRet);
+        }
+
+    public String SetTimeZone(String sTimeZone)
+        {
+        String            sRet = "Unable to set timezone to " + sTimeZone;
+        TimeZone         tz = null;
+        AlarmManager     amgr = null;
+
+        if ((sTimeZone.length() > 0) && (sTimeZone.startsWith("GMT")))
+            {
+            amgr = (AlarmManager) contextWrapper.getSystemService(Context.ALARM_SERVICE);
+            if (amgr != null)
+                amgr.setTimeZone(sTimeZone);
+            }
+        else
+            {
+            String [] zoneNames = TimeZone.getAvailableIDs();
+            int nNumMatches = zoneNames.length;
+            int    lcv = 0;
+
+            for (lcv = 0; lcv < nNumMatches; lcv++)
+                {
+                if (zoneNames[lcv].equalsIgnoreCase(sTimeZone))
+                    break;
+                }
+
+            if (lcv < nNumMatches)
+                {
+                amgr = (AlarmManager) contextWrapper.getSystemService(Context.ALARM_SERVICE);
+                if (amgr != null)
+                    amgr.setTimeZone(zoneNames[lcv]);
+                }
+            }
+
+        if (amgr != null)
+            {
+            tz = TimeZone.getDefault();
+            Date now = new Date();
+            sRet = tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG);
+            }
+
+        return(sRet);
+        }
+
+    public String GetSystemTime()
+        {
+        String sRet = "";
+        Calendar cal = Calendar.getInstance();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS");
+        sRet = sdf.format(cal.getTime());
+
+        return (sRet);
+        }
+
+    public String SetSystemTime(String sDate, String sTime, OutputStream out) {
+        String sRet = "";
+        String sM = "";
+        String sMillis = "";
+        String [] theArgs = new String [3];
+
+        theArgs[0] = "su";
+        theArgs[1] = "-c";
+
+        if (((sDate != null) && (sTime != null)) &&
+            (sDate.contains("/") || sDate.contains(".")) &&
+            (sTime.contains(":"))) {
+            int year = Integer.parseInt(sDate.substring(0,4));
+            int month = Integer.parseInt(sDate.substring(5,7));
+            int day = Integer.parseInt(sDate.substring(8,10));
+
+            int hour = Integer.parseInt(sTime.substring(0,2));
+            int mins = Integer.parseInt(sTime.substring(3,5));
+            int secs = Integer.parseInt(sTime.substring(6,8));
+
+            Calendar cal = new GregorianCalendar(TimeZone.getDefault());
+            cal.set(year, month - 1, day, hour, mins, secs);
+            long lMillisecs = cal.getTime().getTime();
+
+            sM = Long.toString(lMillisecs);
+            sMillis = sM.substring(0, sM.length() - 3) + "." + sM.substring(sM.length() - 3);
+
+        } else if ((sDate != null) && (sTime == null) && sDate.contains(".")) {
+            String serverName = sDate;
+//            String serverName = "us.pool.ntp.org";
+            sRet = "NTP Server: " + serverName + lineSep;
+            // Send request
+            DatagramSocket socket;
+
+            try {
+                socket = new DatagramSocket();
+                InetAddress address = InetAddress.getByName(serverName);
+                byte[] buf = new NtpMessage().toByteArray();
+                DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 123);
+
+                // Set the transmit timestamp *just* before sending the packet
+                // ToDo: Does this actually improve performance or not?
+                NtpMessage.encodeTimestamp(packet.getData(), 40, (System.currentTimeMillis()/1000.0) + 2208988800.0);
+
+                socket.send(packet);
+
+                // Get response
+                System.out.println("NTP request sent, waiting for response...\n");
+                packet = new DatagramPacket(buf, buf.length);
+                socket.receive(packet);
+
+                // Immediately record the incoming timestamp since 00:00 1-JAN-1900 in secs.
+                double destinationTimestamp = (System.currentTimeMillis()/1000.0) + 2208988800.0;
+
+                // Process response
+                NtpMessage msg = new NtpMessage(packet.getData());
+
+                // Corrected, according to RFC2030 errata
+                double roundTripDelay = (destinationTimestamp-msg.originateTimestamp) - (msg.transmitTimestamp-msg.receiveTimestamp);
+
+                double localClockOffset = ((msg.receiveTimestamp - msg.originateTimestamp) + (msg.transmitTimestamp - destinationTimestamp)) / 2;
+
+                // convert base of timestamp from 00:00 1900/01/01 to 00:00:00 1970/01/01
+                double utc = msg.transmitTimestamp - (2208988800.0);
+
+                // convert from secs to ms
+                long lNewMillisecs = (long)(utc * 1000.0);
+
+                // create a date object using the timestamp this will take into account the timezone and daylight savings settings
+                Date dt = new Date(lNewMillisecs);
+
+                sRet += "  Time: " + new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS").format(dt) + lineSep;
+
+                // get the timestamp
+                long lMillisecs = dt.getTime();
+
+                // format the timestamp as required for the date command
+                sM = Long.toString(lMillisecs);
+                sMillis = sM.substring(0, sM.length() - 3) + "." + sM.substring(sM.length() - 3);
+
+                socket.close();
+            } catch (SocketException e) {
+                e.printStackTrace();
+            } catch (UnknownHostException e) {
+                e.printStackTrace();
+                sRet = sErrorPrefix + "Unknown host";
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            sRet += "Invalid argument(s)";
+        }
+
+        // if we have an argument
+        if (sMillis.length() > 0) {
+            theArgs[2] = "date -u " + sMillis;
+
+            try {
+                pProc = Runtime.getRuntime().exec(theArgs);
+                RedirOutputThread outThrd = new RedirOutputThread(pProc, null);
+                outThrd.start();
+                outThrd.join(10000);
+                sRet += GetSystemTime();
+            } catch (IOException e) {
+                sRet = e.getMessage();
+                e.printStackTrace();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return (sRet);
+    }
+
+    public String GetClok()
+        {
+        long lMillisecs = System.currentTimeMillis();
+        String sRet = "";
+
+        if (lMillisecs > 0)
+            sRet = Long.toString(lMillisecs);
+
+        return(sRet);
+        }
+
+    public String GetUptime()
+        {
+        String sRet = "";
+        long lHold = 0;
+        long lUptime = SystemClock.elapsedRealtime();
+        int    nDays = 0;
+        int    nHours = 0;
+        int nMinutes = 0;
+        int nSecs = 0;
+        int nMilliseconds = 0;
+
+        if (lUptime > 0)
+            {
+            nDays = (int)(lUptime / (24L * 60L * 60L * 1000L));
+            lHold = lUptime % (24L * 60L * 60L * 1000L);
+            nHours = (int)(lHold / (60L * 60L * 1000L));
+            lHold %= 60L * 60L * 1000L;
+            nMinutes = (int)(lHold / (60L * 1000L));
+            lHold %= 60L * 1000L;
+            nSecs = (int)(lHold / 1000L);
+            nMilliseconds = (int)(lHold % 1000);
+            sRet = "" + nDays + " days " + nHours + " hours " + nMinutes + " minutes " + nSecs + " seconds " + nMilliseconds + " ms";
+            }
+
+        return (sRet);
+        }
+
+    public String NewKillProc(String sProcId, OutputStream out)
+        {
+        String sRet = "";
+        String [] theArgs = new String [3];
+
+        theArgs[0] = "su";
+        theArgs[1] = "-c";
+        theArgs[2] = "kill " + sProcId;
+
+        try
+            {
+            pProc = Runtime.getRuntime().exec(theArgs);
+            RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
+            outThrd.start();
+            outThrd.join(5000);
+            }
+        catch (IOException e)
+            {
+            sRet = e.getMessage();
+            e.printStackTrace();
+            }
+        catch (InterruptedException e)
+            {
+            e.printStackTrace();
+            }
+
+        return(sRet);
+        }
+
+    public String SendPing(String sIPAddr, OutputStream out)
+        {
+        String sRet = "";
+        String [] theArgs = new String [4];
+
+        theArgs[0] = "ping";
+        theArgs[1] = "-c";
+        theArgs[2] = "3";
+        theArgs[3] = sIPAddr;
+
+        try
+            {
+            pProc = Runtime.getRuntime().exec(theArgs);
+            RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
+            outThrd.start();
+            outThrd.join(5000);
+            if (out == null)
+                sRet = outThrd.strOutput;
+            }
+        catch (IOException e)
+            {
+            sRet = e.getMessage();
+            e.printStackTrace();
+            }
+        catch (InterruptedException e)
+            {
+            e.printStackTrace();
+            }
+
+        return (sRet);
+        }
+
+    public String GetTmpDir()
+    {
+        String     sRet = "";
+        Context ctx = contextWrapper.getApplicationContext();
+        File dir = ctx.getFilesDir();
+        ctx = null;
+        try {
+            sRet = dir.getCanonicalPath();
+            }
+        catch (IOException e)
+            {
+            e.printStackTrace();
+            }
+        return(sRet);
+    }
+
+    public String PrintFileTimestamp(String sFile)
+        {
+        String     sRet = "";
+        String    sTmpFileName = fixFileName(sFile);
+        long    lModified = -1;
+
+        if (sTmpFileName.contains("org.mozilla.fennec") || sTmpFileName.contains("org.mozilla.firefox")) {
+            ContentResolver cr = contextWrapper.getContentResolver();
+            Uri ffxFiles = Uri.parse("content://" + (sTmpFileName.contains("fennec") ? fenProvider : ffxProvider) + "/dir");
+
+            String[] columns = new String[] {
+                    "_id",
+                    "isdir",
+                    "filename",
+                    "length",
+                    "ts"
+                };
+
+            Cursor myCursor = cr.query(    ffxFiles,
+                                        columns,         // Which columns to return
+                                        sTmpFileName,   // Which rows to return (all rows)
+                                        null,           // Selection arguments (none)
+                                        null);            // Order clause (none)
+            if (myCursor != null) {
+                if (myCursor.getCount() > 0) {
+                    if (myCursor.moveToPosition(0)) {
+                        lModified = myCursor.getLong(myCursor.getColumnIndex("ts"));
+                    }
+                }
+                myCursor.close();
+            }
+        }
+        else {
+            File theFile = new File(sTmpFileName);
+
+            if (theFile.exists()) {
+                lModified = theFile.lastModified();
+            }
+        }
+
+        if (lModified != -1) {
+            Date dtModified = new Date(lModified);
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss:SSS");
+            sRet = "Last modified: " + sdf.format(dtModified);
+        }
+        else {
+            sRet = sErrorPrefix + "[" + sTmpFileName + "] doesn't exist";
+        }
+
+        return(sRet);
+        }
+
+    public String GetIniData(String sSection, String sKey, String sFile)
+        {
+        String sRet = "";
+        String sComp = "";
+        String sLine = "";
+        boolean bFound = false;
+        BufferedReader in = null;
+        String    sTmpFileName = fixFileName(sFile);
+
+        try {
+            in = new BufferedReader(new FileReader(sTmpFileName));
+            sComp = "[" + sSection + "]";
+            while ((sLine = in.readLine()) != null)
+                {
+                if (sLine.equalsIgnoreCase(sComp))
+                    {
+                    bFound = true;
+                    break;
+                    }
+                }
+
+            if (bFound)
+                {
+                sComp = (sKey + " =").toLowerCase();
+                while ((sLine = in.readLine()) != null)
+                    {
+                    if (sLine.toLowerCase().contains(sComp))
+                        {
+                        String [] temp = null;
+                        temp = sLine.split("=");
+                        if (temp != null)
+                            {
+                            if (temp.length > 1)
+                                sRet = temp[1].trim();
+                            }
+                        break;
+                        }
+                    }
+                }
+            in.close();
+            }
+        catch (FileNotFoundException e)
+            {
+            sComp = e.toString();
+            }
+        catch (IOException e)
+            {
+            sComp = e.toString();
+            }
+        return (sRet);
+        }
+
+    public String RunReboot(OutputStream out, String sCallBackIP, String sCallBackPort)
+        {
+        String sRet = "";
+        Context ctx = contextWrapper.getApplicationContext();
+        String [] theArgs = new String [3];
+
+        theArgs[0] = "su";
+        theArgs[1] = "-c";
+        theArgs[2] = "reboot";
+        try {
+            if ((sCallBackIP != null) && (sCallBackPort != null) &&
+                (sCallBackIP.length() > 0) && (sCallBackPort.length() > 0))    {
+                FileOutputStream fos = ctx.openFileOutput("update.info", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
+                String sBuffer = sCallBackIP + "," + sCallBackPort + "\rSystem rebooted\r";
+                fos.write(sBuffer.getBytes());
+                fos.flush();
+                fos.close();
+                fos = null;
+            }
+        } catch (FileNotFoundException e) {
+            sRet = sErrorPrefix + "Callback file creation error [rebt] call failed " + e.getMessage();
+            e.printStackTrace();
+        } catch (IOException e) {
+            sRet = sErrorPrefix + "Callback file error [rebt] call failed " + e.getMessage();
+            e.printStackTrace();
+        }
+
+        try {
+            // Tell all of the data channels we are rebooting
+            ((ASMozStub)this.contextWrapper).SendToDataChannel("Rebooting ...");
+
+            pProc = Runtime.getRuntime().exec(theArgs);
+            RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
+            outThrd.start();
+            outThrd.join(10000);
+        } catch (IOException e) {
+            sRet = e.getMessage();
+            e.printStackTrace();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        return (sRet);
+        }
+
+    public String UnInstallApp(String sApp, OutputStream out)
+        {
+        String sRet = "";
+        String [] theArgs = new String [3];
+
+        theArgs[0] = "su";
+        theArgs[1] = "-c";
+        theArgs[2] = "pm uninstall " + sApp + ";reboot;exit";
+
+        try
+            {
+            pProc = Runtime.getRuntime().exec(theArgs);
+
+            RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
+            outThrd.start();
+            outThrd.join(60000);
+            int nRet = pProc.exitValue();
+            sRet = "\nuninst complete [" + nRet + "]";
+            }
+        catch (IOException e)
+            {
+            sRet = e.getMessage();
+            e.printStackTrace();
+            }
+        catch (InterruptedException e)
+            {
+            e.printStackTrace();
+            }
+
+        return (sRet);
+    }
+
+    public String InstallApp(String sApp, OutputStream out)
+        {
+        String sRet = "";
+        String [] theArgs = new String [3];
+        File    srcFile = new File(sApp);
+
+        theArgs[0] = "su";
+        theArgs[1] = "-c";
+        theArgs[2] = "mv " + GetTmpDir() + "/" + srcFile.getName() + " /data/local/tmp/" + srcFile.getName() + ";exit";
+
+        sRet = CopyFile(sApp, GetTmpDir() + "/" + srcFile.getName());
+        try {
+            out.write(sRet.getBytes());
+            out.flush();
+            }
+        catch (IOException e1)
+            {
+            e1.printStackTrace();
+            }
+
+        try
+            {
+            pProc = Runtime.getRuntime().exec(theArgs);
+
+            RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
+            outThrd.start();
+            outThrd.join(90000);
+            int nRet = pProc.exitValue();
+            sRet = "\nmove complete [" + nRet + "]";
+            try
+                {
+                out.write(sRet.getBytes());
+                out.flush();
+                }
+            catch (IOException e1)
+                {
+                e1.printStackTrace();
+                }
+
+            theArgs[2] = "chmod 666 /data/local/tmp/" + srcFile.getName() + ";exit";
+            pProc = Runtime.getRuntime().exec(theArgs);
+            RedirOutputThread outThrd2 = new RedirOutputThread(pProc, out);
+            outThrd2.start();
+            outThrd2.join(10000);
+            int nRet2 = pProc.exitValue();
+            sRet = "\npermission change complete [" + nRet2 + "]\n";
+            try {
+                out.write(sRet.getBytes());
+                out.flush();
+                }
+            catch (IOException e1)
+                {
+                e1.printStackTrace();
+                }
+
+            theArgs[2] = "pm install -r /data/local/tmp/" + srcFile.getName() + " Cleanup" + ";exit";
+            pProc = Runtime.getRuntime().exec(theArgs);
+            RedirOutputThread outThrd3 = new RedirOutputThread(pProc, out);
+            outThrd3.start();
+            outThrd3.join(60000);
+            int nRet3 = pProc.exitValue();
+            sRet = "\ninstallation complete [" + nRet3 + "]";
+            try {
+                out.write(sRet.getBytes());
+                out.flush();
+                }
+            catch (IOException e1)
+                {
+                e1.printStackTrace();
+                }
+
+            theArgs[2] = "rm /data/local/tmp/" + srcFile.getName() + ";exit";
+            pProc = Runtime.getRuntime().exec(theArgs);
+            RedirOutputThread outThrd4 = new RedirOutputThread(pProc, out);
+            outThrd4.start();
+            outThrd4.join(60000);
+            int nRet4 = pProc.exitValue();
+            sRet = "\ntmp file removed [" + nRet4 + "]";
+            try {
+                out.write(sRet.getBytes());
+                out.flush();
+                }
+            catch (IOException e1)
+                {
+                e1.printStackTrace();
+                }
+            sRet = "\nSuccess";
+            }
+        catch (IOException e)
+            {
+            sRet = e.getMessage();
+            e.printStackTrace();
+            }
+        catch (InterruptedException e)
+            {
+            e.printStackTrace();
+            }
+
+        return (sRet);
+        }
+
+    public String StrtUpdtOMatic(String sPkgName, String sPkgFileName, String sCallBackIP, String sCallBackPort)
+        {
+        String sRet = "";
+
+        Context ctx = contextWrapper.getApplicationContext();
+        PackageManager pm = ctx.getPackageManager();
+
+        Intent prgIntent = new Intent();
+        prgIntent.setPackage("com.mozilla.watcher");
+
+        try {
+            PackageInfo pi = pm.getPackageInfo("com.mozilla.watcher", PackageManager.GET_SERVICES | PackageManager.GET_INTENT_FILTERS);
+            ServiceInfo [] si = pi.services;
+            for (int i = 0; i < si.length; i++)
+                {
+                ServiceInfo s = si[i];
+                if (s.name.length() > 0)
+                    {
+                    prgIntent.setClassName(s.packageName, s.name);
+                    break;
+                    }
+                }
+            }
+        catch (NameNotFoundException e)
+            {
+            e.printStackTrace();
+            sRet = sErrorPrefix + "watcher is not properly installed";
+            return(sRet);
+            }
+
+        prgIntent.putExtra("command", "updt");
+        prgIntent.putExtra("pkgName", sPkgName);
+        prgIntent.putExtra("pkgFile", sPkgFileName);
+        prgIntent.putExtra("reboot", true);
+
+        try
+            {
+            if ((sCallBackIP != null) && (sCallBackPort != null) &&
+                (sCallBackIP.length() > 0) && (sCallBackPort.length() > 0))
+                {
+                FileOutputStream fos = ctx.openFileOutput("update.info", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
+                String sBuffer = sCallBackIP + "," + sCallBackPort + "\rupdate started " + sPkgName + " " + sPkgFileName + "\r";
+                fos.write(sBuffer.getBytes());
+                fos.flush();
+                fos.close();
+                fos = null;
+                prgIntent.putExtra("outFile", ctx.getFilesDir() + "/update.info");
+                }
+            else {
+                if (prgIntent.hasExtra("outFile")) {
+                    System.out.println("outFile extra unset from intent");
+                    prgIntent.removeExtra("outFile");
+                }
+            }
+
+            ComponentName cn = contextWrapper.startService(prgIntent);
+            if (cn != null)
+                sRet = "exit";
+            else
+                sRet = sErrorPrefix + "Unable to use watcher service";
+            }
+        catch(ActivityNotFoundException anf)
+            {
+            sRet = sErrorPrefix + "Activity Not Found Exception [updt] call failed";
+            anf.printStackTrace();
+            }
+        catch (FileNotFoundException e)
+            {
+            sRet = sErrorPrefix + "File creation error [updt] call failed";
+            e.printStackTrace();
+            }
+        catch (IOException e)
+            {
+            sRet = sErrorPrefix + "File error [updt] call failed";
+            e.printStackTrace();
+            }
+
+        ctx = null;
+
+        return (sRet);
+        }
+
+    public String StartJavaPrg(String [] sArgs, Intent preIntent)
+        {
+        String sRet = "";
+        String sArgList = "";
+        String sUrl = "";
+//        String sRedirFileName = "";
+        Intent prgIntent = null;
+
+        Context ctx = contextWrapper.getApplicationContext();
+        PackageManager pm = ctx.getPackageManager();
+
+        if (preIntent == null)
+            prgIntent = new Intent();
+        else
+            prgIntent = preIntent;
+
+        prgIntent.setPackage(sArgs[0]);
+        prgIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        try {
+            PackageInfo pi = pm.getPackageInfo(sArgs[0], PackageManager.GET_ACTIVITIES | PackageManager.GET_INTENT_FILTERS);
+            ActivityInfo [] ai = pi.activities;
+            for (int i = 0; i < ai.length; i++)
+                {
+                ActivityInfo a = ai[i];
+                if (a.name.length() > 0)
+                    {
+                    prgIntent.setClassName(a.packageName, a.name);
+                    break;
+                    }
+                }
+            }
+        catch (NameNotFoundException e)
+            {
+            e.printStackTrace();
+            }
+
+        if (sArgs.length > 1)
+            {
+            if (sArgs[0].contains("android.browser"))
+                prgIntent.setAction(Intent.ACTION_VIEW);
+            else
+                prgIntent.setAction(Intent.ACTION_MAIN);
+
+            if (sArgs[0].contains("fennec"))
+                {
+                sArgList = "";
+                sUrl = "";
+
+                for (int lcv = 1; lcv < sArgs.length; lcv++)
+                    {
+                    if (sArgs[lcv].contains("://"))
+                        {
+                        prgIntent.setAction(Intent.ACTION_VIEW);
+                        sUrl = sArgs[lcv];
+                        }
+                    else
+                        {
+                        if (sArgs[lcv].equals(">"))
+                            {
+                            lcv++;
+                            if (lcv < sArgs.length)
+                                lcv++;
+//                                sRedirFileName = sArgs[lcv++];
+                            }
+                        else
+                            sArgList += " " + sArgs[lcv];
+                        }
+                    }
+
+                if (sArgList.length() > 0)
+                    prgIntent.putExtra("args", sArgList.trim());
+
+                if (sUrl.length() > 0)
+                    prgIntent.setData(Uri.parse(sUrl.trim()));
+                }
+            else
+                {
+                for (int lcv = 1; lcv < sArgs.length; lcv++)
+                    sArgList += " " + sArgs[lcv];
+
+                prgIntent.setData(Uri.parse(sArgList.trim()));
+                }
+            }
+        else
+            {
+            prgIntent.setAction(Intent.ACTION_MAIN);
+            }
+
+        try
+            {
+            contextWrapper.startActivity(prgIntent);
+            }
+        catch(ActivityNotFoundException anf)
+            {
+            anf.printStackTrace();
+            }
+
+        ctx = null;
+        return (sRet);
+        }
+
+    public String StartPrg(String [] progArray, OutputStream out)
+        {
+        String sRet = "";
+
+        try
+            {
+            pProc = Runtime.getRuntime().exec(progArray);
+            RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
+            outThrd.start();
+            outThrd.join(10000);
+            int nRetCode = pProc.exitValue();
+            sRet = "return code [" + nRetCode + "]";
+            }
+        catch (IOException e)
+            {
+            e.printStackTrace();
+            }
+        catch (InterruptedException e)
+            {
+            e.printStackTrace();
+            sRet = "Timed out!";
+            }
+
+        return (sRet);
+        }
+
+    public String StartPrg2(String [] progArray, OutputStream out)
+        {
+        String sRet = "";
+
+        int    nArraySize = 0;
+        int    nArgs = progArray.length - 1; // 1st arg is the environment string
+        int    lcv    = 0;
+        int    temp = 0;
+
+        String sEnvString = progArray[0];
+
+        if (!sEnvString.contains("=") && (sEnvString.length() > 0))
+            {
+            if (sEnvString.contains("/") || sEnvString.contains("\\") || !sEnvString.contains("."))
+                sRet = StartPrg(progArray, out);
+            else
+                sRet = StartJavaPrg(progArray, null);
+            return(sRet);
+            }
+
+        // Set up command line args stripping off the environment string
+        String [] theArgs = new String [nArgs];
+        for (lcv = 0; lcv < nArgs; lcv++)
+            {
+            theArgs[lcv] = progArray[lcv + 1];
+            }
+
+        try
+            {
+            String [] envStrings = sEnvString.split(",");
+            Map<String, String> newEnv = new HashMap<String, String>();
+
+            for (lcv = 0; lcv < envStrings.length; lcv++)
+                {
+                temp = envStrings[lcv].indexOf("=");
+                if (temp > 0)
+                    {
+                    newEnv.put(    envStrings[lcv].substring(0, temp),
+                                envStrings[lcv].substring(temp + 1, envStrings[lcv].length()));
+                    }
+                }
+
+            Map<String, String> sysEnv = System.getenv();
+
+            nArraySize = sysEnv.size();
+
+            for (Map.Entry<String, String> entry : newEnv.entrySet())
+                {
+                if (!sysEnv.containsKey(entry.getKey()))
+                    {
+                    nArraySize++;
+                    }
+                }
+
+            String[] envArray = new String[nArraySize];
+
+            int        i = 0;
+            int        offset;
+            String    sKey = "";
+            String     sValue = "";
+
+            for (Map.Entry<String, String> entry : sysEnv.entrySet())
+                {
+                sKey = entry.getKey();
+                if (newEnv.containsKey(sKey))
+                    {
+                    sValue = newEnv.get(sKey);
+                    if ((offset = sValue.indexOf("$" + sKey)) != -1)
+                        {
+                        envArray[i++] = sKey +
+                                        "=" +
+                                        sValue.substring(0, offset) +
+                                        entry.getValue() +
+                                        sValue.substring(offset + sKey.length() + 1);
+                        }
+                    else
+                        envArray[i++] = sKey + "=" + sValue;
+                    newEnv.remove(sKey);
+                    }
+                else
+                    envArray[i++] = entry.getKey() + "=" + entry.getValue();
+                }
+
+            for (Map.Entry<String, String> entry : newEnv.entrySet())
+                {
+                envArray[i++] = entry.getKey() + "=" + entry.getValue();
+                }
+
+            if (theArgs[0].contains("/") || theArgs[0].contains("\\") || !theArgs[0].contains("."))
+                {
+                pProc = Runtime.getRuntime().exec(theArgs, envArray);
+
+                RedirOutputThread outThrd = new RedirOutputThread(pProc, out);
+                outThrd.start();
+                outThrd.join(10000);
+                int nRetCode = pProc.exitValue();
+                sRet = "return code [" + nRetCode + "]";
+                }
+            else
+                {
+                Intent preIntent = new Intent();
+                for (lcv = 0; lcv < envArray.length; lcv++)
+                    {
+                    preIntent.putExtra("env" + lcv, envArray[lcv]);
+                    }
+                sRet = StartJavaPrg(theArgs, preIntent);
+                }
+            }
+        catch(UnsupportedOperationException e)
+            {
+            if (e != null)
+                e.printStackTrace();
+            }
+        catch(ClassCastException e)
+            {
+            if (e != null)
+                e.printStackTrace();
+            }
+        catch(IllegalArgumentException e)
+            {
+            if (e != null)
+                e.printStackTrace();
+            }
+        catch(NullPointerException e)
+            {
+            if (e != null)
+                e.printStackTrace();
+            }
+        catch (IOException e)
+            {
+            e.printStackTrace();
+            }
+        catch (InterruptedException e)
+            {
+            e.printStackTrace();
+            sRet = "Timed out!";
+            }
+
+        return (sRet);
+        }
+
+    private String PrintUsage()
+        {
+        String sRet =
+            "run [cmdline]                - start program no wait\n" +
+            "exec [env pairs] [cmdline]   - start program no wait optionally pass env\n" +
+            "                               key=value pairs (comma separated)\n" +
+            "kill [program name]          - kill program no path\n" +
+            "killall                      - kill all processes started\n" +
+            "ps                           - list of running processes\n" +
+            "info                         - list of device info\n" +
+            "        [os]                 - os version for device\n" +
+            "        [id]                 - unique identifier for device\n" +
+            "        [uptime]             - uptime for device\n" +
+            "        [systime]            - current system time\n" +
+            "        [screen]             - width, height and bits per pixel for device\n" +
+            "        [memory]             - physical, free, available, storage memory\n" +
+            "                               for device\n" +
+            "        [processes]          - list of running processes see 'ps'\n" +
+            "deadman timeout              - set the duration for the deadman timer\n" +
+            "alrt [on/off]                - start or stop sysalert behavior\n" +
+            "disk [arg]                   - prints disk space info\n" +
+            "cp file1 file2               - copy file1 to file2\n" +
+            "time file                    - timestamp for file\n" +
+            "hash file                    - generate hash for file\n" +
+            "cd directory                 - change cwd\n" +
+            "cat file                     - cat file\n" +
+            "cwd                          - display cwd\n" +
+            "mv file1 file2               - move file1 to file2\n" +
+            "push filename                - push file to device\n" +
+            "rm file                      - delete file\n" +
+            "rmdr directory               - delete directory even if not empty\n" +
+            "mkdr directory               - create directory\n" +
+            "dirw directory               - tests whether the directory is writable\n" +
+            "isdir directory              - test whether the directory exists\n" +
+            "stat processid               - stat process\n" +
+            "dead processid               - print whether the process is alive or hung\n" +
+            "mems                         - dump memory stats\n" +
+            "ls                           - print directory\n" +
+            "tmpd                         - print temp directory\n" +
+            "ping [hostname/ipaddr]       - ping a network device\n" +
+            "unzp zipfile destdir         - unzip the zipfile into the destination dir\n" +
+            "zip zipfile src              - zip the source file/dir into zipfile\n" +
+            "rebt                         - reboot device\n" +
+            "inst /path/filename.apk      - install the referenced apk file\n" +
+            "uninst packagename           - uninstall the referenced package\n" +
+            "updt pkgname pkgfile         - unpdate the referenced package\n" +
+            "clok                         - the current device time expressed as the" +
+            "                               number of millisecs since epoch\n" +
+            "settime date time            - sets the device date and time\n" +
+            "                               (YYYY/MM/DD HH:MM:SS)\n" +
+            "tzset timezone               - sets the device timezone format is\n" +
+            "                               GMTxhh:mm x = +/- or a recognized Olsen string\n" +
+            "tzget                        - returns the current timezone set on the device\n" +
+            "rebt                         - reboot device\n" +
+            "quit                         - disconnect SUTAgent\n" +
+            "exit                         - close SUTAgent\n" +
+            "ver                          - SUTAgent version\n" +
+            "help                         - you're reading it";
+        return (sRet);
+        }
+}
--- a/build/mobile/sutagent/android/Makefile.in
+++ b/build/mobile/sutagent/android/Makefile.in
@@ -46,16 +46,17 @@ MODULE = sutAgentAndroid
 
 JAVAFILES = \
   AlertLooperThread.java \
   ASMozStub.java \
   CmdWorkerThread.java \
   DataWorkerThread.java \
   DoAlert.java \
   DoCommand.java \
+  NtpMessage.java \
   Power.java \
   RedirOutputThread.java \
   RunCmdThread.java \
   RunDataThread.java \
   SUTAgentAndroid.java \
   SUTStartupIntentReceiver.java \
   WifiConfiguration.java \
   R.java \
new file mode 100644
--- /dev/null
+++ b/build/mobile/sutagent/android/NtpMessage.java
@@ -0,0 +1,468 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * This class represents a NTP message, as specified in RFC 2030.  The message
+ * format is compatible with all versions of NTP and SNTP.
+ *
+ * This class does not support the optional authentication protocol, and
+ * ignores the key ID and message digest fields.
+ *
+ * For convenience, this class exposes message values as native Java types, not
+ * the NTP-specified data formats.  For example, timestamps are
+ * stored as doubles (as opposed to the NTP unsigned 64-bit fixed point
+ * format).
+ *
+ * However, the contructor NtpMessage(byte[]) and the method toByteArray()
+ * allow the import and export of the raw NTP message format.
+ *
+ *
+ * Usage example
+ *
+ * // Send message
+ * DatagramSocket socket = new DatagramSocket();
+ * InetAddress address = InetAddress.getByName("ntp.cais.rnp.br");
+ * byte[] buf = new NtpMessage().toByteArray();
+ * DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 123);
+ * socket.send(packet);
+ *
+ * // Get response
+ * socket.receive(packet);
+ * System.out.println(msg.toString());
+ *
+ *
+ * This code is copyright (c) Adam Buckley 2004
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.  A HTML version of the GNU General Public License can be
+ * seen at http://www.gnu.org/licenses/gpl.html
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ *
+ * Comments for member variables are taken from RFC2030 by David Mills,
+ * University of Delaware.
+ *
+ * Number format conversion code in NtpMessage(byte[] array) and toByteArray()
+ * inspired by http://www.pps.jussieu.fr/~jch/enseignement/reseaux/
+ * NTPMessage.java which is copyright (c) 2003 by Juliusz Chroboczek
+ *
+ * @author Adam Buckley
+ * ***** END LICENSE BLOCK ***** */
+package com.mozilla.SUTAgentAndroid;
+
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+
+public class NtpMessage
+{
+    /**
+     * This is a two-bit code warning of an impending leap second to be
+     * inserted/deleted in the last minute of the current day.  It's values
+     * may be as follows:
+     *
+     * Value     Meaning
+     * -----     -------
+     * 0         no warning
+     * 1         last minute has 61 seconds
+     * 2         last minute has 59 seconds)
+     * 3         alarm condition (clock not synchronized)
+     */
+    public byte leapIndicator = 0;
+
+
+    /**
+     * This value indicates the NTP/SNTP version number.  The version number
+     * is 3 for Version 3 (IPv4 only) and 4 for Version 4 (IPv4, IPv6 and OSI).
+     * If necessary to distinguish between IPv4, IPv6 and OSI, the
+     * encapsulating context must be inspected.
+     */
+    public byte version = 3;
+
+
+    /**
+     * This value indicates the mode, with values defined as follows:
+     *
+     * Mode     Meaning
+     * ----     -------
+     * 0        reserved
+     * 1        symmetric active
+     * 2        symmetric passive
+     * 3        client
+     * 4        server
+     * 5        broadcast
+     * 6        reserved for NTP control message
+     * 7        reserved for private use
+     *
+     * In unicast and anycast modes, the client sets this field to 3 (client)
+     * in the request and the server sets it to 4 (server) in the reply. In
+     * multicast mode, the server sets this field to 5 (broadcast).
+     */
+    public byte mode = 0;
+
+
+    /**
+     * This value indicates the stratum level of the local clock, with values
+     * defined as follows:
+     *
+     * Stratum  Meaning
+     * ----------------------------------------------
+     * 0        unspecified or unavailable
+     * 1        primary reference (e.g., radio clock)
+     * 2-15     secondary reference (via NTP or SNTP)
+     * 16-255   reserved
+     */
+    public short stratum = 0;
+
+
+    /**
+     * This value indicates the maximum interval between successive messages,
+     * in seconds to the nearest power of two. The values that can appear in
+     * this field presently range from 4 (16 s) to 14 (16284 s); however, most
+     * applications use only the sub-range 6 (64 s) to 10 (1024 s).
+     */
+    public byte pollInterval = 0;
+
+
+    /**
+     * This value indicates the precision of the local clock, in seconds to
+     * the nearest power of two.  The values that normally appear in this field
+     * range from -6 for mains-frequency clocks to -20 for microsecond clocks
+     * found in some workstations.
+     */
+    public byte precision = 0;
+
+
+    /**
+     * This value indicates the total roundtrip delay to the primary reference
+     * source, in seconds.  Note that this variable can take on both positive
+     * and negative values, depending on the relative time and frequency
+     * offsets. The values that normally appear in this field range from
+     * negative values of a few milliseconds to positive values of several
+     * hundred milliseconds.
+     */
+    public double rootDelay = 0;
+
+
+    /**
+     * This value indicates the nominal error relative to the primary reference
+     * source, in seconds.  The values  that normally appear in this field
+     * range from 0 to several hundred milliseconds.
+     */
+    public double rootDispersion = 0;
+
+
+    /**
+     * This is a 4-byte array identifying the particular reference source.
+     * In the case of NTP Version 3 or Version 4 stratum-0 (unspecified) or
+     * stratum-1 (primary) servers, this is a four-character ASCII string, left
+     * justified and zero padded to 32 bits. In NTP Version 3 secondary
+     * servers, this is the 32-bit IPv4 address of the reference source. In NTP
+     * Version 4 secondary servers, this is the low order 32 bits of the latest
+     * transmit timestamp of the reference source. NTP primary (stratum 1)
+     * servers should set this field to a code identifying the external
+     * reference source according to the following list. If the external
+     * reference is one of those listed, the associated code should be used.
+     * Codes for sources not listed can be contrived as appropriate.
+     *
+     * Code     External Reference Source
+     * ----     -------------------------
+     * LOCL     uncalibrated local clock used as a primary reference for
+     *          a subnet without external means of synchronization
+     * PPS      atomic clock or other pulse-per-second source
+     *          individually calibrated to national standards
+     * ACTS     NIST dialup modem service
+     * USNO     USNO modem service
+     * PTB      PTB (Germany) modem service
+     * TDF      Allouis (France) Radio 164 kHz
+     * DCF      Mainflingen (Germany) Radio 77.5 kHz
+     * MSF      Rugby (UK) Radio 60 kHz
+     * WWV      Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz
+     * WWVB     Boulder (US) Radio 60 kHz
+     * WWVH     Kaui Hawaii (US) Radio 2.5, 5, 10, 15 MHz
+     * CHU      Ottawa (Canada) Radio 3330, 7335, 14670 kHz
+     * LORC     LORAN-C radionavigation system
+     * OMEG     OMEGA radionavigation system
+     * GPS      Global Positioning Service
+     * GOES     Geostationary Orbit Environment Satellite
+     */
+    public byte[] referenceIdentifier = {0, 0, 0, 0};
+
+
+    /**
+     * This is the time at which the local clock was last set or corrected, in
+     * seconds since 00:00 1-Jan-1900.
+     */
+    public double referenceTimestamp = 0;
+
+
+    /**
+     * This is the time at which the request departed the client for the
+     * server, in seconds since 00:00 1-Jan-1900.
+     */
+    public double originateTimestamp = 0;
+
+
+    /**
+     * This is the time at which the request arrived at the server, in seconds
+     * since 00:00 1-Jan-1900.
+     */
+    public double receiveTimestamp = 0;
+
+
+    /**
+     * This is the time at which the reply departed the server for the client,
+     * in seconds since 00:00 1-Jan-1900.
+     */
+    public double transmitTimestamp = 0;
+
+
+
+    /**
+     * Constructs a new NtpMessage from an array of bytes.
+     */
+    public NtpMessage(byte[] array)
+    {
+        // See the packet format diagram in RFC 2030 for details
+        leapIndicator = (byte) ((array[0] >> 6) & 0x3);
+        version = (byte) ((array[0] >> 3) & 0x7);
+        mode = (byte) (array[0] & 0x7);
+        stratum = unsignedByteToShort(array[1]);
+        pollInterval = array[2];
+        precision = array[3];
+
+        rootDelay = (array[4] * 256.0) +
+            unsignedByteToShort(array[5]) +
+            (unsignedByteToShort(array[6]) / 256.0) +
+            (unsignedByteToShort(array[7]) / 65536.0);
+
+        rootDispersion = (unsignedByteToShort(array[8]) * 256.0) +
+            unsignedByteToShort(array[9]) +
+            (unsignedByteToShort(array[10]) / 256.0) +
+            (unsignedByteToShort(array[11]) / 65536.0);
+
+        referenceIdentifier[0] = array[12];
+        referenceIdentifier[1] = array[13];
+        referenceIdentifier[2] = array[14];
+        referenceIdentifier[3] = array[15];
+
+        referenceTimestamp = decodeTimestamp(array, 16);
+        originateTimestamp = decodeTimestamp(array, 24);
+        receiveTimestamp = decodeTimestamp(array, 32);
+        transmitTimestamp = decodeTimestamp(array, 40);
+    }
+
+
+
+    /**
+     * Constructs a new NtpMessage in client -> server mode, and sets the
+     * transmit timestamp to the current time.
+     */
+    public NtpMessage()
+    {
+        // Note that all the other member variables are already set with
+        // appropriate default values.
+        this.mode = 3;
+        this.transmitTimestamp = (System.currentTimeMillis()/1000.0) + 2208988800.0;
+    }
+
+
+
+    /**
+     * This method constructs the data bytes of a raw NTP packet.
+     */
+    public byte[] toByteArray()
+    {
+        // All bytes are automatically set to 0
+        byte[] p = new byte[48];
+
+        p[0] = (byte) (leapIndicator << 6 | version << 3 | mode);
+        p[1] = (byte) stratum;
+        p[2] = (byte) pollInterval;
+        p[3] = (byte) precision;
+
+        // root delay is a signed 16.16-bit FP, in Java an int is 32-bits
+        int l = (int) (rootDelay * 65536.0);
+        p[4] = (byte) ((l >> 24) & 0xFF);
+        p[5] = (byte) ((l >> 16) & 0xFF);
+        p[6] = (byte) ((l >> 8) & 0xFF);
+        p[7] = (byte) (l & 0xFF);
+
+        // root dispersion is an unsigned 16.16-bit FP, in Java there are no
+        // unsigned primitive types, so we use a long which is 64-bits
+        long ul = (long) (rootDispersion * 65536.0);
+        p[8] = (byte) ((ul >> 24) & 0xFF);
+        p[9] = (byte) ((ul >> 16) & 0xFF);
+        p[10] = (byte) ((ul >> 8) & 0xFF);
+        p[11] = (byte) (ul & 0xFF);
+
+        p[12] = referenceIdentifier[0];
+        p[13] = referenceIdentifier[1];
+        p[14] = referenceIdentifier[2];
+        p[15] = referenceIdentifier[3];
+
+        encodeTimestamp(p, 16, referenceTimestamp);
+        encodeTimestamp(p, 24, originateTimestamp);
+        encodeTimestamp(p, 32, receiveTimestamp);
+        encodeTimestamp(p, 40, transmitTimestamp);
+
+        return p;
+    }
+
+
+
+    /**
+     * Returns a string representation of a NtpMessage
+     */
+    public String toString()
+    {
+        String precisionStr =
+            new DecimalFormat("0.#E0").format(Math.pow(2, precision));
+
+        return "Leap indicator: " + leapIndicator + "\n" +
+            "Version: " + version + "\n" +
+            "Mode: " + mode + "\n" +
+            "Stratum: " + stratum + "\n" +
+            "Poll: " + pollInterval + "\n" +
+            "Precision: " + precision + " (" + precisionStr + " seconds)\n" +
+            "Root delay: " + new DecimalFormat("0.00").format(rootDelay*1000) + " ms\n" +
+            "Root dispersion: " + new DecimalFormat("0.00").format(rootDispersion*1000) + " ms\n" +
+            "Reference identifier: " + referenceIdentifierToString(referenceIdentifier, stratum, version) + "\n" +
+            "Reference timestamp: " + timestampToString(referenceTimestamp) + "\n" +
+            "Originate timestamp: " + timestampToString(originateTimestamp) + "\n" +
+            "Receive timestamp:   " + timestampToString(receiveTimestamp) + "\n" +
+            "Transmit timestamp:  " + timestampToString(transmitTimestamp);
+    }
+
+
+
+    /**
+     * Converts an unsigned byte to a short.  By default, Java assumes that
+     * a byte is signed.
+     */
+    public static short unsignedByteToShort(byte b)
+    {
+        if((b & 0x80)==0x80) return (short) (128 + (b & 0x7f));
+        else return (short) b;
+    }
+
+
+
+    /**
+     * Will read 8 bytes of a message beginning at <code>pointer</code>
+     * and return it as a double, according to the NTP 64-bit timestamp
+     * format.
+     */
+    public static double decodeTimestamp(byte[] array, int pointer)
+    {
+        double r = 0.0;
+
+        for(int i=0; i<8; i++)
+        {
+            r += unsignedByteToShort(array[pointer+i]) * Math.pow(2, (3-i)*8);
+        }
+
+        return r;
+    }
+
+
+
+    /**
+     * Encodes a timestamp in the specified position in the message
+     */
+    public static void encodeTimestamp(byte[] array, int pointer, double timestamp)
+    {
+        // Converts a double into a 64-bit fixed point
+        for(int i=0; i<8; i++)
+        {
+            // 2^24, 2^16, 2^8, .. 2^-32
+            double base = Math.pow(2, (3-i)*8);
+
+            // Capture byte value
+            array[pointer+i] = (byte) (timestamp / base);
+
+            // Subtract captured value from remaining total
+            timestamp = timestamp - (double) (unsignedByteToShort(array[pointer+i]) * base);
+        }
+
+        // From RFC 2030: It is advisable to fill the non-significant
+        // low order bits of the timestamp with a random, unbiased
+        // bitstring, both to avoid systematic roundoff errors and as
+        // a means of loop detection and replay detection.
+        array[7] = (byte) (Math.random()*255.0);
+    }
+
+
+
+    /**
+     * Returns a timestamp (number of seconds since 00:00 1-Jan-1900) as a
+     * formatted date/time string.
+     */
+    public static String timestampToString(double timestamp)
+    {
+        if(timestamp==0) return "0";
+
+        // timestamp is relative to 1900, utc is used by Java and is relative
+        // to 1970
+        double utc = timestamp - (2208988800.0);
+
+        // milliseconds
+        long ms = (long) (utc * 1000.0);
+
+        // date/time
+        String date = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss").format(new Date(ms));
+
+        // fraction
+        double fraction = timestamp - ((long) timestamp);
+        String fractionSting = new DecimalFormat(".000000").format(fraction);
+
+        return date + fractionSting;
+    }
+
+
+
+    /**
+     * Returns a string representation of a reference identifier according
+     * to the rules set out in RFC 2030.
+     */
+    public static String referenceIdentifierToString(byte[] ref, short stratum, byte version)
+    {
+        // From the RFC 2030:
+        // In the case of NTP Version 3 or Version 4 stratum-0 (unspecified)
+        // or stratum-1 (primary) servers, this is a four-character ASCII
+        // string, left justified and zero padded to 32 bits.
+        if(stratum==0 || stratum==1)
+        {
+            return new String(ref);
+        }
+
+        // In NTP Version 3 secondary servers, this is the 32-bit IPv4
+        // address of the reference source.
+        else if(version==3)
+        {
+            return unsignedByteToShort(ref[0]) + "." +
+                unsignedByteToShort(ref[1]) + "." +
+                unsignedByteToShort(ref[2]) + "." +
+                unsignedByteToShort(ref[3]);
+        }
+
+        // In NTP Version 4 secondary servers, this is the low order 32 bits
+        // of the latest transmit timestamp of the reference source.
+        else if(version==4)
+        {
+            return "" + ((unsignedByteToShort(ref[0]) / 256.0) +
+                (unsignedByteToShort(ref[1]) / 65536.0) +
+                (unsignedByteToShort(ref[2]) / 16777216.0) +
+                (unsignedByteToShort(ref[3]) / 4294967296.0));
+        }
+
+        return "";
+    }
+}
--- a/build/mobile/sutagent/android/Power.java
+++ b/build/mobile/sutagent/android/Power.java
@@ -1,102 +1,102 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * 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.os;
-
-import java.io.IOException;
-
-/**
- * Class that provides access to some of the power management functions.
- *
- * {@hide}
- */
-public class Power
-{
-    // can't instantiate this class
-    private Power()
-    {
-    }
-
-    /**
-     * Wake lock that ensures that the CPU is running.  The screen might
-     * not be on.
-     */
-    public static final int PARTIAL_WAKE_LOCK = 1;
-
-    /**
-     * Wake lock that ensures that the screen is on.
-     */
-    public static final int FULL_WAKE_LOCK = 2;
-
-    public static native void acquireWakeLock(int lock, String id);
-    public static native void releaseWakeLock(String id);
-
-    /**
-     * Brightness value for fully off
-     */
-    public static final int BRIGHTNESS_OFF = 0;
-
-    /**
-     * Brightness value for dim backlight
-     */
-    public static final int BRIGHTNESS_DIM = 20;
-
-    /**
-     * Brightness value for fully on
-     */
-    public static final int BRIGHTNESS_ON = 255;
-
-    /**
-     * Brightness value to use when battery is low
-     */
-    public static final int BRIGHTNESS_LOW_BATTERY = 10;
-
-    /**
-     * Threshold for BRIGHTNESS_LOW_BATTERY (percentage)
-     * Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD
-     */
-    public static final int LOW_BATTERY_THRESHOLD = 10;
-
-    /**
-     * Turn the screen on or off
-     *
-     * @param on Whether you want the screen on or off
-     */
-    public static native int setScreenState(boolean on);
-
-    public static native int setLastUserActivityTimeout(long ms);
-
-    /**
-     * Turn the device off.
-     *
-     * This method is considered deprecated in favor of
-     * {@link android.policy.ShutdownThread.shutdownAfterDisablingRadio()}.
-     *
-     * @deprecated
-     * @hide
-     */
-    @Deprecated
-    public static native void shutdown();
-
-    /**
-     * Reboot the device.
-     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
-     *
-     * @throws IOException if reboot fails for some reason (eg, lack of
-     *         permission)
-     */
-    public static native void reboot(String reason) throws IOException;
-}
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.os;
+
+import java.io.IOException;
+
+/**
+ * Class that provides access to some of the power management functions.
+ *
+ * {@hide}
+ */
+public class Power
+{
+    // can't instantiate this class
+    private Power()
+    {
+    }
+
+    /**
+     * Wake lock that ensures that the CPU is running.  The screen might
+     * not be on.
+     */
+    public static final int PARTIAL_WAKE_LOCK = 1;
+
+    /**
+     * Wake lock that ensures that the screen is on.
+     */
+    public static final int FULL_WAKE_LOCK = 2;
+
+    public static native void acquireWakeLock(int lock, String id);
+    public static native void releaseWakeLock(String id);
+
+    /**
+     * Brightness value for fully off
+     */
+    public static final int BRIGHTNESS_OFF = 0;
+
+    /**
+     * Brightness value for dim backlight
+     */
+    public static final int BRIGHTNESS_DIM = 20;
+
+    /**
+     * Brightness value for fully on
+     */
+    public static final int BRIGHTNESS_ON = 255;
+
+    /**
+     * Brightness value to use when battery is low
+     */
+    public static final int BRIGHTNESS_LOW_BATTERY = 10;
+
+    /**
+     * Threshold for BRIGHTNESS_LOW_BATTERY (percentage)
+     * Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD
+     */
+    public static final int LOW_BATTERY_THRESHOLD = 10;
+
+    /**
+     * Turn the screen on or off
+     *
+     * @param on Whether you want the screen on or off
+     */
+    public static native int setScreenState(boolean on);
+
+    public static native int setLastUserActivityTimeout(long ms);
+
+    /**
+     * Turn the device off.
+     *
+     * This method is considered deprecated in favor of
+     * {@link android.policy.ShutdownThread.shutdownAfterDisablingRadio()}.
+     *
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    public static native void shutdown();
+
+    /**
+     * Reboot the device.
+     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
+     *
+     * @throws IOException if reboot fails for some reason (eg, lack of
+     *         permission)
+     */
+    public static native void reboot(String reason) throws IOException;
+}
--- a/build/mobile/sutagent/android/R.java
+++ b/build/mobile/sutagent/android/R.java
@@ -1,32 +1,33 @@
-/* AUTO-GENERATED FILE.  DO NOT MODIFY.
- *
- * This class was automatically generated by the
- * aapt tool from the resource data it found.  It
- * should not be modified by hand.
- */
-
-package com.mozilla.SUTAgentAndroid;
-
-public final class R {
-    public static final class attr {
-    }
-    public static final class drawable {
-        public static final int ateamlogo=0x7f020000;
-        public static final int ic_stat_first=0x7f020001;
-        public static final int ic_stat_neterror=0x7f020002;
-        public static final int ic_stat_second=0x7f020003;
-        public static final int ic_stat_warning=0x7f020004;
-        public static final int icon=0x7f020005;
-    }
-    public static final class id {
-        public static final int Button01=0x7f050001;
-        public static final int Textview01=0x7f050000;
-    }
-    public static final class layout {
-        public static final int main=0x7f030000;
-    }
-    public static final class string {
-        public static final int app_name=0x7f040001;
-        public static final int hello=0x7f040000;
-    }
-}
+/* AUTO-GENERATED FILE.  DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found.  It
+ * should not be modified by hand.
+ */
+
+package com.mozilla.SUTAgentAndroid;
+
+public final class R {
+    public static final class attr {
+    }
+    public static final class drawable {
+        public static final int ateamlogo=0x7f020000;
+        public static final int ic_stat_first=0x7f020001;
+        public static final int ic_stat_neterror=0x7f020002;
+        public static final int ic_stat_second=0x7f020003;
+        public static final int ic_stat_warning=0x7f020004;
+        public static final int icon=0x7f020005;
+    }
+    public static final class id {
+        public static final int Button01=0x7f050001;
+        public static final int Textview01=0x7f050000;
+    }
+    public static final class layout {
+        public static final int main=0x7f030000;
+    }
+    public static final class string {
+        public static final int app_name=0x7f040001;
+        public static final int foreground_service_started=0x7f040002;
+        public static final int hello=0x7f040000;
+    }
+}
--- a/build/mobile/sutagent/android/RedirOutputThread.java
+++ b/build/mobile/sutagent/android/RedirOutputThread.java
@@ -1,164 +1,163 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Android SUTAgent code.
- *
- * The Initial Developer of the Original Code is
- * Bob Moss.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Bob Moss <bmoss@mozilla.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package com.mozilla.SUTAgentAndroid.service;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-
-public class RedirOutputThread extends Thread
-	{
-	OutputStream out;
-	InputStream	sutErr;
-	InputStream	sutOut;
-	Process pProc;
-	String	strOutput;
-	
-	public RedirOutputThread(Process pProc, OutputStream out)
-		{
-		if (pProc != null)
-			{
-			this.pProc = pProc;
-			sutErr = pProc.getErrorStream(); // Stderr
-			sutOut = pProc.getInputStream(); // Stdout
-			}
-		if (out != null)
-			this.out = out;
-		
-		strOutput = "";
-		}
-	
-	public void run()
-		{
-		boolean bStillRunning = true;
-		int	nBytesOut = 0;
-		int nBytesErr = 0;
-		int nBytesRead = 0;
-		PrintWriter pOut = null;
-		byte[] buffer = new byte[1024];
-		
-		if (out != null)
-			pOut = new PrintWriter(out);
-		else
-			bStillRunning = true;
-		
-		while (bStillRunning) 
-			{
-			try 
-				{
-				if ((nBytesOut = sutOut.available()) > 0)
-					{
-					if (nBytesOut > buffer.length)
-						{
-						buffer = null;
-						System.gc();
-						buffer = new byte[nBytesOut];
-						}
-					nBytesRead = sutOut.read(buffer, 0, nBytesOut);
-					if (nBytesRead == -1)
-						bStillRunning = false;
-					else 
-						{
-						String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n");
-						if (pOut != null)
-							{
-							pOut.print(sRep);
-							pOut.flush();
-							}
-						else
-							strOutput += sRep;
-						}
-					}
-
-				if ((nBytesErr = sutErr.available()) > 0)
-					{
-					if (nBytesErr > buffer.length)
-						{
-						buffer = null;
-						System.gc();
-						buffer = new byte[nBytesErr];
-						}
-					nBytesRead = sutErr.read(buffer, 0, nBytesErr);
-					if (nBytesRead == -1)
-						bStillRunning = false;
-					else 
-						{
-						String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n");
-						if (pOut != null)
-							{
-							pOut.print(sRep);
-							pOut.flush();
-							}
-						else
-							strOutput += sRep;
-						}
-					}
-
-				bStillRunning = (IsProcRunning(pProc) || (sutOut.available() > 0) || (sutErr.available() > 0));
-				} 
-			catch (IOException e)
-				{
-//				Toast.makeText(SUTAgentAndroid.me.getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
-				e.printStackTrace();
-				}
-			}
-		
-		pProc.destroy();
-		buffer = null;
-		System.gc();
-		}
-	
-	private boolean IsProcRunning(Process pProc)
-		{
-		boolean bRet = false;
-		@SuppressWarnings("unused")
-		int nExitCode = 0;
-	
-		try
-			{
-			nExitCode = pProc.exitValue();
-			}
-		catch (IllegalThreadStateException z)
-			{	
-			bRet = true;
-			}
-
-		return(bRet);
-		}
-	}
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android SUTAgent code.
+ *
+ * The Initial Developer of the Original Code is
+ * Bob Moss.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package com.mozilla.SUTAgentAndroid.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+public class RedirOutputThread extends Thread
+    {
+    OutputStream out;
+    InputStream    sutErr;
+    InputStream    sutOut;
+    Process pProc;
+    String    strOutput;
+    int    nExitCode = -1;
+
+    public RedirOutputThread(Process pProc, OutputStream out)
+        {
+        if (pProc != null)
+            {
+            this.pProc = pProc;
+            sutErr = pProc.getErrorStream(); // Stderr
+            sutOut = pProc.getInputStream(); // Stdout
+            }
+        if (out != null)
+            this.out = out;
+
+        strOutput = "";
+        }
+
+    public void run()
+        {
+        boolean bStillRunning = true;
+        int    nBytesOut = 0;
+        int nBytesErr = 0;
+        int nBytesRead = 0;
+        PrintWriter pOut = null;
+        byte[] buffer = new byte[1024];
+
+        if (out != null)
+            pOut = new PrintWriter(out);
+        else
+            bStillRunning = true;
+
+        while (bStillRunning)
+            {
+            try
+                {
+                if ((nBytesOut = sutOut.available()) > 0)
+                    {
+                    if (nBytesOut > buffer.length)
+                        {
+                        buffer = null;
+                        System.gc();
+                        buffer = new byte[nBytesOut];
+                        }
+                    nBytesRead = sutOut.read(buffer, 0, nBytesOut);
+                    if (nBytesRead == -1)
+                        bStillRunning = false;
+                    else
+                        {
+                        String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n");
+                        if (pOut != null)
+                            {
+                            pOut.print(sRep);
+                            pOut.flush();
+                            }
+                        else
+                            strOutput += sRep;
+                        }
+                    }
+
+                if ((nBytesErr = sutErr.available()) > 0)
+                    {
+                    if (nBytesErr > buffer.length)
+                        {
+                        buffer = null;
+                        System.gc();
+                        buffer = new byte[nBytesErr];
+                        }
+                    nBytesRead = sutErr.read(buffer, 0, nBytesErr);
+                    if (nBytesRead == -1)
+                        bStillRunning = false;
+                    else
+                        {
+                        String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n");
+                        if (pOut != null)
+                            {
+                            pOut.print(sRep);
+                            pOut.flush();
+                            }
+                        else
+                            strOutput += sRep;
+                        }
+                    }
+
+                bStillRunning = (IsProcRunning(pProc) || (sutOut.available() > 0) || (sutErr.available() > 0));
+                }
+            catch (IOException e)
+                {
+                e.printStackTrace();
+                }
+            }
+
+        pProc.destroy();
+        buffer = null;
+        System.gc();
+        }
+
+    private boolean IsProcRunning(Process pProc)
+        {
+        boolean bRet = false;
+
+        try
+            {
+            nExitCode = pProc.exitValue();
+            }
+        catch (IllegalThreadStateException z)
+            {
+            nExitCode = -1;
+            bRet = true;
+            }
+
+        return(bRet);
+        }
+    }
--- a/build/mobile/sutagent/android/RunCmdThread.java
+++ b/build/mobile/sutagent/android/RunCmdThread.java
@@ -1,318 +1,318 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Android SUTAgent code.
- *
- * The Initial Developer of the Original Code is
- * Bob Moss.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Bob Moss <bmoss@mozilla.com>
- * 
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-package com.mozilla.SUTAgentAndroid.service;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.mozilla.SUTAgentAndroid.R;
-import com.mozilla.SUTAgentAndroid.SUTAgentAndroid;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-
-public class RunCmdThread extends Thread
-	{
-	private ServerSocket SvrSocket = null;
-	private Socket socket	= null;
-	private Handler handler = null;
-	boolean bListening	= true;
-	boolean bNetError = false;
-	List<CmdWorkerThread> theWorkers = new ArrayList<CmdWorkerThread>();
-	android.app.Service	svc = null;
-
-	public RunCmdThread(ServerSocket socket, android.app.Service service, Handler handler)
-		{
-		super("RunCmdThread");
-		this.SvrSocket = socket;
-		this.svc = service;
-		this.handler = handler;
-		}
-	
-	public void StopListening()
-		{
-		bListening = false;
-		}
-	
-	public void run() {
-		try {
-			int	nIterations = 0;
-			
-			SvrSocket.setSoTimeout(5000);
-			while (bListening)
-				{
-				try 
-					{
-					socket = SvrSocket.accept();
-					CmdWorkerThread theWorker = new CmdWorkerThread(this, socket);
-					theWorker.start();
-					theWorkers.add(theWorker);
-					}
-				catch (SocketTimeoutException toe)
-					{
-					if (++nIterations > 60)
-						{
-						nIterations = 0;
-						String sRet = SendPing("www.mozilla.org");
-						if (sRet.contains("3 received"))
-							handler.post(new doCancelNotification());
-						else
-							handler.post(new doSendNotification("SUTAgent - Network Connectivity Error", sRet));
-						sRet = null;
-						}
-					continue;
-					}
-				catch (IOException e)
-					{
-					e.printStackTrace();
-					continue;
-					}
-				}
-			
-			int nNumWorkers = theWorkers.size();
-			for (int lcv = 0; lcv < nNumWorkers; lcv++)
-				{
-				if (theWorkers.get(lcv).isAlive())
-					{
-					theWorkers.get(lcv).StopListening();
-					while(theWorkers.get(lcv).isAlive())
-						;
-					}
-				}
-			
-			theWorkers.clear();
-			
-			SvrSocket.close();
-			
-			svc.stopSelf();
-			
-//			SUTAgentAndroid.me.finish();
-			} 
-		catch (IOException e)
-			{
-		    e.printStackTrace();
-			}
-		return;
-		}
-	
-	private String SendPing(String sIPAddr)
-		{
-		Process	pProc;
-		String sRet = "";
-		String [] theArgs = new String [4];
-		boolean bStillRunning = true;
-		int	nBytesOut = 0;
-		int nBytesErr = 0;
-		int nBytesRead = 0;
-		byte[] buffer = new byte[1024];
-	
-		theArgs[0] = "ping";
-		theArgs[1] = "-c";
-		theArgs[2] = "3";
-		theArgs[3] = sIPAddr;
-	
-		try 
-			{
-			pProc = Runtime.getRuntime().exec(theArgs);
-			
-			InputStream sutOut = pProc.getInputStream();
-			InputStream sutErr = pProc.getErrorStream();
-			
-			while (bStillRunning) 
-				{
-				try 
-					{
-					if ((nBytesOut = sutOut.available()) > 0)
-						{
-						if (nBytesOut > buffer.length)
-							{
-							buffer = null;
-							System.gc();
-							buffer = new byte[nBytesOut];
-							}
-						nBytesRead = sutOut.read(buffer, 0, nBytesOut);
-						if (nBytesRead == -1)
-							bStillRunning = false;
-						else 
-							{
-							String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n");
-							sRet += sRep;
-							sRep = null;
-							}
-						}
-
-					if ((nBytesErr = sutErr.available()) > 0)
-						{
-						if (nBytesErr > buffer.length)
-							{
-							buffer = null;
-							System.gc();
-							buffer = new byte[nBytesErr];
-							}
-						nBytesRead = sutErr.read(buffer, 0, nBytesErr);
-						if (nBytesRead == -1)
-							bStillRunning = false;
-						else 
-							{
-							String sRep = new String(buffer,0,nBytesRead).replace("\n", "\r\n");
-							sRet += sRep;
-							sRep = null;
-							}
-						}
-
-					bStillRunning = (IsProcRunning(pProc) || (sutOut.available() > 0) || (sutErr.available() > 0));
-					} 
-				catch (IOException e)
-					{
-					e.printStackTrace();
-					}
-				
-				if ((bStillRunning == true) && (nBytesErr == 0) && (nBytesOut == 0))
-					{
-					try {
-						sleep(2000);
-						}
-					catch (InterruptedException e) {
-						e.printStackTrace();
-						}
-					}
-				}
-
-			pProc.destroy();
-			pProc = null;
-			}
-		catch (IOException e) 
-			{
-			sRet = e.getMessage();
-			e.printStackTrace();
-			}
-
-		return (sRet);
-		}
-	
-	private boolean IsProcRunning(Process pProc)
-		{
-		boolean bRet = false;
-		@SuppressWarnings("unused")
-		int nExitCode = 0;
-
-		try
-			{
-			nExitCode = pProc.exitValue();
-			}
-		catch (IllegalThreadStateException z)
-			{	
-			bRet = true;
-			}
-		catch (Exception e)
-			{
-			e.printStackTrace();
-			}
-
-		return(bRet);
-		}
-
-	private void SendNotification(String tickerText, String expandedText)
-		{
-		NotificationManager notificationManager = (NotificationManager)svc.getSystemService(Context.NOTIFICATION_SERVICE);
-		
-//		int icon = android.R.drawable.stat_notify_more;
-//		int icon = R.drawable.ic_stat_first;
-//		int icon = R.drawable.ic_stat_second;
-//		int icon = R.drawable.ic_stat_neterror;
-		int icon = R.drawable.ateamlogo;
-		long when = System.currentTimeMillis();
-		
-		Notification notification = new Notification(icon, tickerText, when);
-		
-		notification.flags |= (Notification.FLAG_INSISTENT | Notification.FLAG_AUTO_CANCEL);
-		notification.defaults |= Notification.DEFAULT_SOUND;
-		notification.defaults |= Notification.DEFAULT_VIBRATE;
-		notification.defaults |= Notification.DEFAULT_LIGHTS;
-		
-		Context context = svc.getApplicationContext();
-		
-		// Intent to launch an activity when the extended text is clicked
-		Intent intent2 = new Intent(svc, SUTAgentAndroid.class);
-		PendingIntent launchIntent = PendingIntent.getActivity(context, 0, intent2, 0);
-		
-		notification.setLatestEventInfo(context, tickerText, expandedText, launchIntent);
-				
-		notificationManager.notify(1959, notification);
-		}
-	
-	private void CancelNotification()
-		{
-		NotificationManager notificationManager = (NotificationManager)svc.getSystemService(Context.NOTIFICATION_SERVICE);
-		notificationManager.cancel(1959);
-		}
-	
-	class doCancelNotification implements Runnable
-		{
-		public void run()
-			{
-			CancelNotification();
-			}
-		};
-
-	class doSendNotification implements Runnable
-		{
-		private String sTitle = "";
-		private String sBText = "";
-		
-		doSendNotification(String sTitle, String sBodyText)
-			{
-			this.sTitle = sTitle;
-			this.sBText = sBodyText;
-			}
-
-		public void run() 
-			{
-			SendNotification(sTitle, sBText);
-			}
-		};
-}
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android SUTAgent code.
+ *
+ * The Initial Developer of the Original Code is
+ * Bob Moss.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Bob Moss <bmoss@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package com.mozilla.SUTAgentAndroid.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.mozilla.SUTAgentAndroid.R;
+import com.mozilla.SUTAgentAndroid.SUTAgentAndroid;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+
+public class RunCmdThread extends Thread
+    {
+    private ServerSocket SvrSocket = null;