Skip to content

Commit

Permalink
Command dispatching
Browse files Browse the repository at this point in the history
  - code to parse and dispatch commands added
  - ResponseWriter utility class added
  - Password command implementation added

Several bugfixes
  - wakelock added
  - preferences properly loaded (first start)
  - rebinding removed
  • Loading branch information
Lukas Vacek committed Feb 25, 2015
1 parent f1fff1d commit 9e49237
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 54 deletions.
1 change: 1 addition & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_SATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
android:name=".MockGeoFixApp"
Expand Down
60 changes: 60 additions & 0 deletions app/src/main/java/github/luv/mockgeofix/CommandDispatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package github.luv.mockgeofix;

import android.content.Context;
import android.util.Log;

import java.nio.channels.SocketChannel;

import github.luv.mockgeofix.command.PasswordCommand;
import github.luv.mockgeofix.util.ResponseWriter;

public class CommandDispatcher {
String TAG = "CommandDispatcher";

static private CommandDispatcher instance = new CommandDispatcher();
static public CommandDispatcher getInstance() { return instance; }
private CommandDispatcher() {}

protected Context mContext;
protected PasswordCommand passwordCommand;

static public void init(Context context) {
getInstance()._init(context);
}

static public void dispatch(SocketChannel client, String command) {
getInstance()._verifyInitiated();
getInstance()._dispatch(client, command);
}

protected void _init(Context context) {
if (mContext != null) {
throw new AssertionError("CommandDispatcher.init called twice!");
}
mContext = context;
passwordCommand = new PasswordCommand(context);
}

protected void _dispatch(SocketChannel client, String command) {
String cmd = command.split(" ", 2)[0].toLowerCase();
if (cmd.equals("password")) {
passwordCommand.execute(client, command);
} else if (cmd.equals("geo")) {
if ( passwordCommand.passwordRequired() && (! passwordCommand.loggedIn(client)) ) {
ResponseWriter.notLoggedIn(client);
} else {
Log.i(TAG, "GEO FIX::" + command);
}
} else if (cmd.equals("help")) {
Log.i(TAG, "GEO HELP::" + command);
} else {
ResponseWriter.unknownCommand(client);
}
}

private void _verifyInitiated() {
if (mContext == null) {
throw new AssertionError("CommandDispatcher.init has not been called!");
}
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/github/luv/mockgeofix/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ protected void onCreate(Bundle savedInstanceState) {

@Override
protected void onDestroy() {
super.onDestroy();
if (mService != null && mConn != null) {
unbindService(mConn);
}
unregisterReceiver(receiver);
super.onDestroy();
}

protected synchronized void updateListensOn() {
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/github/luv/mockgeofix/MockGeoFixApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import android.app.Application;
import android.content.Intent;
import android.preference.PreferenceManager;

public class MockGeoFixApp extends Application {
@Override
public void onCreate() {
PreferenceManager.setDefaultValues(this, R.xml.pref, false);
CommandDispatcher.init(getApplicationContext());
Intent i = new Intent(getApplicationContext(), MockLocationService.class);
startService(i);
}
Expand Down
104 changes: 51 additions & 53 deletions app/src/main/java/github/luv/mockgeofix/MockLocationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.Log;

Expand All @@ -28,7 +29,7 @@
import java.util.HashMap;
import java.util.Vector;

public class MockLocationService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener {
public class MockLocationService extends Service {
String TAG = "MockLocationService";

protected MockLocationThread mThread = null;
Expand All @@ -52,8 +53,6 @@ public void onCreate() {
super.onCreate();
pref = PreferenceManager.getDefaultSharedPreferences(
getApplicationContext());

pref.registerOnSharedPreferenceChangeListener(this);
}

@Override
Expand All @@ -66,16 +65,6 @@ public IBinder onBind(Intent intent) {
return mBinder;
}

@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("listen_port") || key.equals("listen_ip")) {
if (mThread != null) {
mThread.rebind();
mThread.interrupt();
}
}
}

/* interface to be used by clients */
public boolean isRunning() {
return !(mThread == null);
Expand All @@ -97,7 +86,7 @@ public void stop() {
}
/* end of interface */

/* methods used my MockLocationThread
/* methods used by MockLocationThread
to communicate its' state */
protected void threadHasStopped() {
mThread = null;
Expand All @@ -116,7 +105,7 @@ protected Vector<SocketAddress> getBindAddresses() {
Vector<SocketAddress> ret = new Vector<>();

/* retrieve port settings from preferences */
String portStr = pref.getString("listen_port", "");
String portStr = pref.getString("listen_port", "5554");
int port;
try {
port = Integer.parseInt(portStr);
Expand Down Expand Up @@ -171,16 +160,17 @@ class MockLocationThread extends Thread {
public MockLocationThread(Context context, MockLocationService service) {
mContext = context;
mService = service;
PowerManager mgr = (PowerManager)context
.getSystemService(Context.POWER_SERVICE);
mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MockGeoFixWakeLock");
}
private boolean mStop = false;
private boolean mRebind = false;

private HashMap<Socket, Boolean> mClientDiscard = new HashMap<>();
private HashMap<Socket, ByteBuffer> mClientBuffers = new HashMap<>();

public void rebind() {
mRebind = true;
}
protected PowerManager.WakeLock mWakeLock = null;

public void kill() {
mStop = true;
Expand All @@ -190,44 +180,41 @@ public void kill() {
@Override
public void run() {
super.run();
Selector selector = null;
try {
selector = Selector.open();
mBindAddresses = mService.getBindAddresses();
for (SocketAddress address : mBindAddresses) {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.socket().setReuseAddress(true);
ssc.socket().bind(address);
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
try {
mWakeLock.acquire();
} catch (SecurityException ex) {
Log.e(TAG, "WakeLock not acquired - permission denied for WakeLock.");
}
mService.threadHasStartedSuccessfully();

while (!mStop) {
Selector selector = Selector.open();
mBindAddresses = mService.getBindAddresses();
for (SocketAddress address : mBindAddresses) {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.socket().setReuseAddress(true);
ssc.socket().bind(address);
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
mService.threadHasStartedSuccessfully();
try {
while (!mStop && !mRebind) {
//Log.d(TAG, "running");
selector.select();
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable() && key.channel() instanceof ServerSocketChannel) {
// accept connection
SocketChannel client = ((ServerSocketChannel)key.channel()).accept();
if (client != null) {
client.configureBlocking(false);
client.socket().setTcpNoDelay(true);
client.register(selector, SelectionKey.OP_READ);
}
}
if (key.isReadable() && key.channel() instanceof SocketChannel) {
onIncomingData((SocketChannel) key.channel());
}
selector.select();
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable() && key.channel() instanceof ServerSocketChannel) {
// accept connection
SocketChannel client = ((ServerSocketChannel) key.channel()).accept();
if (client != null) {
client.configureBlocking(false);
client.socket().setTcpNoDelay(true);
client.register(selector, SelectionKey.OP_READ);
}
selector.selectedKeys().clear();
}
} finally {
for (SelectionKey key : selector.keys()) {
try { key.channel().close(); } catch (IOException ignored) {}
if (key.isReadable() && key.channel() instanceof SocketChannel) {
onIncomingData((SocketChannel) key.channel());
}
selector.close();
}
selector.selectedKeys().clear();
}
} catch (SocketException e) {
Log.e(TAG, e.toString() );
Expand All @@ -240,6 +227,15 @@ public void run() {
} catch (IOException e) {
Log.e(TAG, e.toString() );
} finally {
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
if (selector != null) {
for (SelectionKey key : selector.keys()) {
try { key.channel().close(); } catch (IOException ignored) {}
}
try { selector.close(); } catch (IOException ignored) {}
}
mService.threadHasStopped();
}
}
Expand Down Expand Up @@ -292,7 +288,8 @@ void processBuffer(SocketChannel client, ByteBuffer buffer) {
while (line != null) {
// process line
try {
Log.i(TAG, "::"+new String(line, "UTF-8"));
String command = new String(line, "UTF-8").replace("\r\n","").replace("\n","");
CommandDispatcher.dispatch(client, command);
} catch (UnsupportedEncodingException ignored) {}
line = getLine(buffer);
}
Expand All @@ -302,7 +299,8 @@ void processBuffer(SocketChannel client, ByteBuffer buffer) {
line = new byte[buffer.limit()];
buffer.get(line);
try {
Log.i(TAG, "::"+new String(line, "UTF-8"));
String command = new String(line, "UTF-8").replace("\r\n","").replace("\n","");
CommandDispatcher.dispatch(client, command);
} catch (UnsupportedEncodingException ignored) {}
buffer.clear();
mClientDiscard.put(client.socket(), Boolean.TRUE);
Expand Down
41 changes: 41 additions & 0 deletions app/src/main/java/github/luv/mockgeofix/SettingsActivity.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package github.luv.mockgeofix;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
Expand All @@ -23,14 +28,41 @@ public class SettingsActivity extends PreferenceActivity
implements Preference.OnPreferenceChangeListener {
static final String TAG = "SettingsActivity";

private MockLocationService mService = null;
private boolean mShowNeedsReset = false;
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mService = ((MockLocationService.Binder)binder).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
};

@Override
protected void onDestroy() {
if (mService != null && mConn != null) {
unbindService(mConn);
}
super.onDestroy();
}

@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
bindService(new Intent(getApplicationContext(),MockLocationService.class),
mConn,
Context.BIND_AUTO_CREATE
);
setupSimplePreferencesScreen();
mShowNeedsReset = false;
bindPreference(findPreference("listen_port"));
bindPreference(findPreference("listen_ip"));
bindPreference(findPreference("password"));
bindPreference(findPreference("require_password"));
mShowNeedsReset = true;
}


Expand Down Expand Up @@ -145,6 +177,15 @@ public boolean onPreferenceChange(Preference pref, Object value) {
pref.setSummary(stringValue);
}
}

/* Warn the user that the change won't take effect until the service is restarted */
if (pref.getKey().equals("listen_port") || pref.getKey().equals("listen_ip")) {
if (mShowNeedsReset && (mService != null && mService.isRunning()) ) {
Toast.makeText(getApplicationContext(), getString(R.string.note_needsreset),
Toast.LENGTH_LONG).show();
}
}

return true;
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/github/luv/mockgeofix/command/Command.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package github.luv.mockgeofix.command;

import java.nio.channels.SocketChannel;

public interface Command {
public void execute(SocketChannel client, String command);
}
Loading

0 comments on commit 9e49237

Please sign in to comment.