blob: 7ecf2f2e085949132f4a42513f035de5dbba3a24 [file] [log] [blame]
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.mozilla.SUTAgentAndroid.service;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.Timer;
import com.mozilla.SUTAgentAndroid.SUTAgentAndroid;
import com.mozilla.SUTAgentAndroid.R;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
public class ASMozStub extends android.app.Service {
private final static int COMMAND_PORT = 20701;
private final static int DATA_PORT = 20700;
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;
boolean doZeroConfig = false;
@SuppressWarnings("unchecked")
private static final Class<?>[] mSetForegroundSignature = new Class[] {
boolean.class};
@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 mSetForeground;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mSetForegroundArgs = new Object[1];
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) {
// Might be running on an older platform.
mStartForeground = mStopForeground = null;
Log.w("SUTAgent", "unable to find start/stopForeground method(s) -- older platform?");
}
try {
mSetForeground = getClass().getMethod("setForeground", mSetForegroundSignature);
}
catch (NoSuchMethodException e) {
mSetForeground = null;
Log.e("SUTAgent", "unable to find setForeground method!");
}
doToast("Listener Service created...");
}
WifiManager.MulticastLock multicastLock;
JmDNS jmdns;
void startZeroConf() {
if (multicastLock == null) {
WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
multicastLock = wifi.createMulticastLock("SUTAgent");
multicastLock.setReferenceCounted(true);
}
multicastLock.acquire();
try {
InetAddress inetAddress = SUTAgentAndroid.getLocalInetAddress();
if (jmdns == null) {
jmdns = JmDNS.create(inetAddress, null);
}
if (jmdns != null) {
String name = "SUTAgent";
String hwid = SUTAgentAndroid.getHWID(this);
if (hwid != null) {
name += " [hwid:" + hwid + "]";
}
// multicast reception is broken for some reason, so
// this service can't be resolved; it can only be
// broadcast. So, we cheat -- we put the IP address
// in the broadcast that we can pull out later.
// However, periods aren't legal, so replace them.
// The IP address will show up as [ip:127_0_0_1]
name += " [ip:" + inetAddress.getHostAddress().toString().replace('.', '_') + "]";
final ServiceInfo serviceInfo = ServiceInfo.create("_sutagent._tcp.local.",
name,
COMMAND_PORT,
"Android SUTAgent");
final JmDNS dns = jmdns;
// we want to call registerService on a new thread, because it can block
// for a little while.
Thread registerThread = new Thread() {
public void run() {
try {
dns.registerService(serviceInfo);
} catch (IOException e) {
Log.e("SUTAgent", "Failed to register JmDNS service!", e);
}
}
};
registerThread.setDaemon(true);
registerThread.start();
}
} catch (IOException e) {
Log.e("SUTAgent", "Failed to register JmDNS service!", e);
}
}
void stopZeroConf() {
if (jmdns != null) {
try {
jmdns.unregisterAllServices();
jmdns.close();
} catch (IOException e) {
Log.e("SUTAgent", "Failed to close JmDNS service!", e);
}
jmdns = null;
}
if (multicastLock != null) {
multicastLock.release();
multicastLock = null;
}
}
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
try {
cmdChnl = new ServerSocket(COMMAND_PORT);
runCmdThrd = new RunCmdThread(cmdChnl, this, handler);
runCmdThrd.start();
doToast(String.format("Command channel port %d ...", COMMAND_PORT));
dataChnl = new ServerSocket(DATA_PORT);
runDataThrd = new RunDataThread(dataChnl, this);
runDataThrd.start();
doToast(String.format("Data channel port %d ...", DATA_PORT));
DoCommand tmpdc = new DoCommand(getApplication());
File dir = getFilesDir();
File iniFile = new File(dir, "SUTAgent.ini");
String sIniFile = iniFile.getAbsolutePath();
String zeroconf = tmpdc.GetIniData("General", "ZeroConfig", sIniFile);
if (zeroconf != "" && Integer.parseInt(zeroconf) == 1) {
this.doZeroConfig = true;
}
if (this.doZeroConfig) {
startZeroConf();
}
Notification notification = new Notification();
startForegroundCompat(R.string.foreground_service_started, notification);
}
catch (Exception e) {
doToast(e.toString());
}
return;
}
public void onDestroy()
{
super.onDestroy();
if (this.doZeroConfig) {
stopZeroConf();
}
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.e("SUTAgent", "Unable to invoke startForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.e("SUTAgent", "Unable to invoke startForeground", e);
}
return;
}
// Fall back on the old API.
if (mSetForeground != null) {
try {
mSetForegroundArgs[0] = Boolean.TRUE;
mSetForeground.invoke(this, mSetForegroundArgs);
} catch (IllegalArgumentException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
}
}
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.e("SUTAgent", "Unable to invoke stopForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.e("SUTAgent", "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);
if (mSetForeground != null) {
try {
mSetForegroundArgs[0] = Boolean.FALSE;
mSetForeground.invoke(this, mSetForegroundArgs);
} catch (IllegalArgumentException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
}
}
}
}