org.harleydroid.HarleyDroidService.java Source code

Java tutorial

Introduction

Here is the source code for org.harleydroid.HarleyDroidService.java

Source

//
// HarleyDroid: Harley Davidson J1850 Data Analyser for Android.
//
// Copyright (C) 2010-2012 Stelian Pop <stelian@popies.net>
//
// 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 3 of the License, or
// (at your option) any later version.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

package org.harleydroid;

import java.lang.ref.WeakReference;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

public class HarleyDroidService extends Service {
    private static final boolean D = false;
    private static final String TAG = HarleyDroidService.class.getSimpleName();

    private static final int MSG_NONE = 0;
    private static final int MSG_CONNECTED = 1;
    private static final int MSG_DISCONNECTED = 2;
    private static final int MSG_START_POLL = 3;
    private static final int MSG_STARTED_POLL = 4;
    private static final int MSG_START_SEND = 5;
    private static final int MSG_STARTED_SEND = 6;
    private static final int MSG_SET_SEND = 7;
    private static final int MSG_DISCONNECT = 8;

    private static final int STATE_DISCONNECT = 1;
    private static final int STATE_TO_DISCONNECT = 2;
    private static final int STATE_CONNECT = 3;
    private static final int STATE_TO_CONNECT = 4;
    private static final int STATE_POLL = 5;
    private static final int STATE_TO_POLL = 6;
    private static final int STATE_SEND = 7;
    private static final int STATE_TO_SEND = 8;
    private static final int STATE_WAIT_RECONNECT = 9;

    private final IBinder binder = new HarleyDroidServiceBinder();
    private HarleyDroidLogger mLogger = null;
    private HarleyData mHD;
    private int mNotifyId;
    private NotificationCompat.Builder mNotifyBuilder;
    private NotificationManager mNotificationManager;
    private Handler mHandler = null;
    private String mInterfaceType = null;
    private BluetoothDevice mDevice = null;
    private J1850Interface mInterface = null;
    private boolean mLogging = false;
    private boolean mMetric = true;
    private boolean mGPS = true;
    private boolean mLogRaw = false;
    private boolean mLogUnknown = false;
    private boolean mAutoReconnect = false;
    private Thread mReconThread = null;
    private int mReconnectDelay = 0;
    private int mCurrentState = STATE_DISCONNECT;
    private int mWantedState = STATE_DISCONNECT;
    private String mSendType[];
    private String mSendTA[];
    private String mSendSA[];
    private String mSendCommand[];
    private String mSendExpect[];
    private int mSendTimeout[];
    private int mSendDelay;
    protected SharedPreferences mPrefs;
    private Handler mServiceHandler;

    @Override
    public void onCreate() {
        super.onCreate();
        if (D)
            Log.d(TAG, "onCreate()");

        mPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
        mHD = new HarleyData(mPrefs);
        mServiceHandler = new HarleyDroidServiceHandler(this);

        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mNotifyId = 1;
        mNotifyBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_stat_notify_harleydroid)
                .setContentTitle(getText(R.string.notification_label)).setOngoing(true).setContentIntent(
                        PendingIntent.getActivity(this, 0, new Intent(this, HarleyDroidDashboard.class), 0));
        notify(R.string.notification_connecting);
        startForeground(mNotifyId, mNotifyBuilder.build());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (D)
            Log.d(TAG, "onDestroy()");
    }

    public void doDestroy() {
        if (D)
            Log.d(TAG, "doDestroy()");

        doDisconnect();
        mNotificationManager.cancel(mNotifyId);
        mNotificationManager = null;
        mHD.destroy();
        stopSelf();
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (D)
            Log.d(TAG, "onBind()");

        return binder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (D)
            Log.d(TAG, "onStartCommand()");

        return START_STICKY;
    }

    public class HarleyDroidServiceBinder extends Binder {
        HarleyDroidService getService() {
            return HarleyDroidService.this;
        }
    }

    public HarleyData getHarleyData() {
        return mHD;
    }

    public void setHandler(Handler handler) {
        if (D)
            Log.d(TAG, "setHandler()");

        mHandler = handler;
    }

    public void setLogging(boolean logging, boolean metric, boolean gps, boolean logRaw, boolean logUnknown) {
        mLogging = logging;
        mMetric = metric;
        mGPS = gps;
        mLogRaw = logRaw;
        mLogUnknown = logUnknown;
    }

    public void setAutoReconnect(boolean autoReconnect, int reconnectDelay) {
        mAutoReconnect = autoReconnect;
        mReconnectDelay = reconnectDelay;
    }

    public void setInterfaceType(String interfaceType, BluetoothDevice dev) {
        boolean reconnect = (mInterface != null);

        if (mInterfaceType == null || !mInterfaceType.equals(interfaceType)
                || (!HarleyDroid.EMULATOR && (mDevice == null || !mDevice.getAddress().equals(dev.getAddress())))) {

            if (D)
                Log.d(TAG, "setInterfaceType(" + interfaceType + ")");

            mInterfaceType = interfaceType;
            mDevice = dev;
            if (reconnect)
                doDisconnect();
            if (HarleyDroid.EMULATOR)
                mInterface = new EmulatorInterface(this);
            else {
                if (interfaceType.equals("elm327"))
                    mInterface = new ELM327Interface(this, dev);
                else if (interfaceType.equals("hdi"))
                    mInterface = new HarleyDroidInterface(this, dev);
            }
            mCurrentState = STATE_DISCONNECT;
            if (reconnect)
                stateMachine();
        }
    }

    public boolean isInitialized() {
        return mInterface != null;
    }

    private void notify(int id) {
        if (mNotificationManager != null) {
            CharSequence text = getText(id);
            mNotifyBuilder.setContentText(text);
            mNotificationManager.notify(mNotifyId, mNotifyBuilder.build());
        }
    }

    private void doDisconnect() {
        if (mReconThread != null)
            mReconThread.interrupt();
        if (mLogger != null) {
            mHD.removeHarleyDataDashboardListener(mLogger);
            mHD.removeHarleyDataDiagnosticsListener(mLogger);
            mHD.removeHarleyDataRawListener(mLogger);
            mLogger.stop();
            mLogger = null;
        }
        if (mInterface != null)
            mInterface.disconnect();
    }

    private void stateMachine() {
        if (D)
            Log.d(TAG, "stateMachine(): transition from " + mCurrentState + " to " + mWantedState);

        if (mCurrentState == mWantedState)
            return;

        switch (mWantedState) {
        case STATE_CONNECT:
            switch (mCurrentState) {
            case STATE_DISCONNECT:
            case STATE_WAIT_RECONNECT:
                mCurrentState = STATE_TO_CONNECT;
                notify(R.string.notification_connecting);
                mHandler.obtainMessage(HarleyDroid.STATUS_CONNECTING, -1, -1).sendToTarget();
                mInterface.connect(mHD);
                return;
            case STATE_POLL:
            case STATE_SEND:
            case STATE_TO_DISCONNECT:
            case STATE_TO_CONNECT:
            case STATE_TO_POLL:
            case STATE_TO_SEND:
                /* nothing to done, wait for state to settle */
                return;
            }
            break;
        case STATE_DISCONNECT:
            switch (mCurrentState) {
            case STATE_CONNECT:
            case STATE_SEND:
            case STATE_POLL:
            case STATE_WAIT_RECONNECT:
                doDestroy();
                return;
            case STATE_TO_DISCONNECT:
            case STATE_TO_CONNECT:
            case STATE_TO_POLL:
            case STATE_TO_SEND:
                /* nothing to done, wait for state to settle */
                return;
            }
            return;
        case STATE_POLL:
            switch (mCurrentState) {
            case STATE_DISCONNECT:
            case STATE_WAIT_RECONNECT:
                mCurrentState = STATE_TO_CONNECT;
                notify(R.string.notification_connecting);
                mHandler.obtainMessage(HarleyDroid.STATUS_CONNECTING, -1, -1).sendToTarget();
                mInterface.connect(mHD);
                return;
            case STATE_CONNECT:
            case STATE_SEND:
                mCurrentState = STATE_TO_POLL;
                mInterface.startPoll();
                return;
            case STATE_TO_DISCONNECT:
            case STATE_TO_CONNECT:
            case STATE_TO_POLL:
            case STATE_TO_SEND:
                /* nothing to done, wait for state to settle */
                return;
            }
            break;
        case STATE_SEND:
            switch (mCurrentState) {
            case STATE_DISCONNECT:
            case STATE_WAIT_RECONNECT:
                mCurrentState = STATE_TO_CONNECT;
                notify(R.string.notification_connecting);
                mHandler.obtainMessage(HarleyDroid.STATUS_CONNECTING, -1, -1).sendToTarget();
                mInterface.connect(mHD);
                return;
            case STATE_CONNECT:
            case STATE_POLL:
                mCurrentState = STATE_TO_SEND;
                mInterface.startSend(mSendType, mSendTA, mSendSA, mSendCommand, mSendExpect, mSendTimeout,
                        mSendDelay);
                return;
            case STATE_TO_DISCONNECT:
            case STATE_TO_CONNECT:
            case STATE_TO_POLL:
            case STATE_TO_SEND:
                /* nothing to done, wait for state to settle */
                return;
            }
            break;
        case STATE_WAIT_RECONNECT:
            switch (mCurrentState) {
            case STATE_DISCONNECT:
                mCurrentState = STATE_WAIT_RECONNECT;
                return;
            case STATE_CONNECT:
            case STATE_POLL:
            case STATE_TO_DISCONNECT:
            case STATE_TO_CONNECT:
            case STATE_TO_POLL:
            case STATE_TO_SEND:
                /* nothing to done, wait for state to settle */
                return;
            }
            break;
        }
        if (D)
            Log.d(TAG, "stateMachine(): bad state transition from " + mCurrentState + " to " + mWantedState);
    }

    public void handleMessage(Message msg) {
        if (D)
            Log.d(TAG, "handleMessage " + msg.what);

        switch (msg.what) {
        case MSG_NONE:
            mReconThread = null;
            mWantedState = msg.arg1;
            break;
        case MSG_CONNECTED:
            mHandler.obtainMessage(HarleyDroid.STATUS_CONNECTED, -1, -1).sendToTarget();
            HarleyDroidService.this.notify(R.string.notification_connected);
            J1850.resetCounters();
            mCurrentState = STATE_CONNECT;
            if (mLogging) {
                mLogger = new HarleyDroidLogger(HarleyDroidService.this, mMetric, mGPS, mLogRaw, mLogUnknown);
                mLogger.start();
                mHD.addHarleyDataDashboardListener(mLogger);
                mHD.addHarleyDataDiagnosticsListener(mLogger);
                mHD.addHarleyDataRawListener(mLogger);
            }
            break;
        case MSG_DISCONNECT:
            mHandler.obtainMessage(HarleyDroid.STATUS_NONE, -1, -1).sendToTarget();
            mWantedState = STATE_DISCONNECT;
            break;
        case MSG_DISCONNECTED:
            mHandler.obtainMessage(msg.arg1, -1, -1).sendToTarget();
            mCurrentState = STATE_DISCONNECT;
            mHD.savePersistentData();
            if (mAutoReconnect) {
                final int lastState = mWantedState;
                HarleyDroidService.this.notify(R.string.notification_autorecon);
                mHandler.obtainMessage(HarleyDroid.STATUS_AUTORECON, -1, -1).sendToTarget();

                doDisconnect();
                mReconThread = new Thread() {
                    public void run() {
                        setName("HarleyDroidService: reconThread");
                        try {
                            Thread.sleep(mReconnectDelay * 1000);
                            //mServiceHandler.removeCallbacksAndMessages(null);
                            mServiceHandler.obtainMessage(MSG_NONE, lastState, -1).sendToTarget();
                            // try again to go to mWantedState
                        } catch (InterruptedException e) {
                        }
                    }
                };
                mWantedState = STATE_WAIT_RECONNECT;
                mReconThread.start();
            } else {
                doDestroy();
                return;
            }
            break;
        case MSG_START_POLL:
            mWantedState = STATE_POLL;
            break;
        case MSG_STARTED_POLL:
            HarleyDroidService.this.notify(R.string.notification_polling);
            mCurrentState = STATE_POLL;
            break;
        case MSG_START_SEND:
            mWantedState = STATE_SEND;
            mSendType = msg.getData().getStringArray("type");
            mSendTA = msg.getData().getStringArray("ta");
            mSendSA = msg.getData().getStringArray("sa");
            mSendCommand = msg.getData().getStringArray("command");
            mSendExpect = msg.getData().getStringArray("expect");
            mSendTimeout = msg.getData().getIntArray("timeout");
            mSendDelay = msg.getData().getInt("delay");
            break;
        case MSG_STARTED_SEND:
            HarleyDroidService.this.notify(R.string.notification_diagnostics);
            mCurrentState = STATE_SEND;
            break;
        case MSG_SET_SEND:
            mInterface.setSendData(msg.getData().getStringArray("type"), msg.getData().getStringArray("ta"),
                    msg.getData().getStringArray("sa"), msg.getData().getStringArray("command"),
                    msg.getData().getStringArray("expect"), msg.getData().getIntArray("timeout"),
                    msg.getData().getInt("delay"));
        }
        stateMachine();
    }

    public void connected() {
        if (D)
            Log.d(TAG, "connected()");
        //mServiceHandler.removeCallbacksAndMessages(null);
        mServiceHandler.obtainMessage(MSG_CONNECTED, -1, -1).sendToTarget();
    }

    public void disconnected(int error) {
        if (D)
            Log.d(TAG, "disconnected()" + mCurrentState + " " + mWantedState);
        //mServiceHandler.removeCallbacksAndMessages(null);
        mServiceHandler.obtainMessage(MSG_DISCONNECTED, error, -1).sendToTarget();
    }

    public void disconnect() {
        if (D)
            Log.d(TAG, "disconnect()" + mCurrentState + " " + mWantedState);
        //mServiceHandler.removeCallbacksAndMessages(null);
        mServiceHandler.obtainMessage(MSG_DISCONNECT, -1, -1).sendToTarget();
    }

    public void startPoll() {
        if (D)
            Log.d(TAG, "startPoll()");
        //mServiceHandler.removeCallbacksAndMessages(null);
        mServiceHandler.obtainMessage(MSG_START_POLL, -1, -1).sendToTarget();
    }

    public void startedPoll() {
        if (D)
            Log.d(TAG, "startedPoll()");
        //mServiceHandler.removeCallbacksAndMessages(null);
        mServiceHandler.obtainMessage(MSG_STARTED_POLL, -1, -1).sendToTarget();
    }

    public void startSend(String type[], String ta[], String sa[], String command[], String expect[], int timeout[],
            int delay) {
        if (D)
            Log.d(TAG, "send()");
        Message m = mServiceHandler.obtainMessage(MSG_START_SEND);
        Bundle b = new Bundle();
        b.putStringArray("type", type);
        b.putStringArray("ta", ta);
        b.putStringArray("sa", sa);
        b.putStringArray("command", command);
        b.putStringArray("expect", expect);
        b.putIntArray("timeout", timeout);
        b.putInt("delay", delay);
        m.setData(b);
        //mServiceHandler.removeCallbacksAndMessages(null);
        m.sendToTarget();
    }

    public void startedSend() {
        if (D)
            Log.d(TAG, "sendDone()");
        //mServiceHandler.removeCallbacksAndMessages(null);
        mServiceHandler.obtainMessage(MSG_STARTED_SEND, -1, -1).sendToTarget();
    }

    public void setSendData(String type[], String ta[], String sa[], String command[], String expect[],
            int timeout[], int delay) {
        if (D)
            Log.d(TAG, "setSendData()");
        Message m = mServiceHandler.obtainMessage(MSG_SET_SEND);
        Bundle b = new Bundle();
        b.putStringArray("type", type);
        b.putStringArray("ta", ta);
        b.putStringArray("sa", sa);
        b.putStringArray("command", command);
        b.putStringArray("expect", expect);
        b.putIntArray("timeout", timeout);
        b.putInt("delay", delay);
        m.setData(b);
        //mServiceHandler.removeCallbacksAndMessages(null);
        m.sendToTarget();
    }

    public boolean isPolling() {
        if (D)
            Log.d(TAG, "isPolling()");

        return (mCurrentState == STATE_POLL);
    }

    public boolean isSending() {
        if (D)
            Log.d(TAG, "isSending()");

        return (mCurrentState == STATE_SEND);
    }

    static class HarleyDroidServiceHandler extends Handler {
        private final WeakReference<HarleyDroidService> mHarleyDroidService;

        HarleyDroidServiceHandler(HarleyDroidService harleyDroidService) {
            mHarleyDroidService = new WeakReference<HarleyDroidService>(harleyDroidService);
        }

        @Override
        public void handleMessage(Message msg) {
            HarleyDroidService hds = mHarleyDroidService.get();
            if (hds != null)
                hds.handleMessage(msg);
        }
    }
}