/*
HvcCommManager.java
Copyright (c) 2015 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.hvc.comm;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.util.Log;
import org.deviceconnect.android.deviceplugin.hvc.BuildConfig;
import org.deviceconnect.android.deviceplugin.hvc.humandetect.HumanDetectEvent;
import org.deviceconnect.android.deviceplugin.hvc.humandetect.HumanDetectEventUtils;
import org.deviceconnect.android.deviceplugin.hvc.humandetect.HumanDetectKind;
import org.deviceconnect.android.deviceplugin.hvc.humandetect.HumanDetectRequestParams;
import org.deviceconnect.android.deviceplugin.hvc.profile.HvcConstants;
import org.deviceconnect.android.deviceplugin.hvc.request.HvcDetectRequestParams;
import org.deviceconnect.android.deviceplugin.hvc.response.HvcResponseUtils;
import org.deviceconnect.android.event.Event;
import org.deviceconnect.android.event.EventManager;
import org.deviceconnect.android.message.DConnectMessageService;
import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.HumanDetectionProfile;
import org.deviceconnect.message.DConnectMessage;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import omron.HVC.HVC;
import omron.HVC.HVCBleCallback;
import omron.HVC.HVC_BLE;
import omron.HVC.HVC_PRM;
import omron.HVC.HVC_RES;
/**
* HVC Communication Manager.
*
* @author NTT DOCOMO, INC.
*/
public class HvcCommManager {
/**
* log tag.
*/
private static final String TAG = HvcCommManager.class.getSimpleName();
/**
* Debug.
*/
private static final Boolean DEBUG = BuildConfig.DEBUG;
/**
* Context.
*/
private DConnectMessageService mContext;
/**
* ServiceId.
*/
private String mServiceId;
/**
* Bluetooth device data.
*/
private BluetoothDevice mBluetoothDevice;
/**
* HVC BLE class.
*/
private HVC_BLE mHvcBle = new HVC_BLE();
/**
* HVC parameter class.
*/
private HVC_PRM mHvcPrm = null;
/**
* HVC response class.
*/
private HVC_RES mHvcRes = new HVC_RES();
/**
* Cache parameter.
*/
private HVC_PRM mCacheHvcPrm = null;
/**
* Request parameters.
*/
private HumanDetectRequestParams mRequestParams = null;
/**
* last process time(System.currentTimeMillis()).
*/
private long mLastAccessTime;
/**
* HVC detect listener.
*/
private HvcDetectListener mListener;
/**
* Constructor.
*
* @param context context
* @param serviceId serviceId
* @param bluetoothDevice bluetoothDevice
*/
public HvcCommManager(final DConnectMessageService context, final String serviceId, final BluetoothDevice bluetoothDevice) {
mContext = context;
mServiceId = serviceId;
mBluetoothDevice = bluetoothDevice;
}
/**
* get serviceId.
*
* @return serviceId.
*/
public String getServiceId() {
return mServiceId;
}
/**
* get bluetooth device data.
* @return bluetooth device data.
*/
public BluetoothDevice getBluetoothDevice() {
return mBluetoothDevice;
}
//
// Register / Unregister Event.
//
/**
* event array.
*/
private final List<HumanDetectEvent> mEventArray = new ArrayList<>();
/**
* register detect event.
*
* @param detectKind detectKind
* @param requestParams request parameters.
* @param response response
* @param origin origin
*/
public void registerDetectEvent(final HumanDetectKind detectKind, final HumanDetectRequestParams requestParams,
final Intent response, final String origin) {
if (DEBUG) {
Log.d(TAG, "registerDetectEvent() detectKind:" + detectKind.toString() + " origin:" + origin);
}
// check already event register info registered.
if (HumanDetectEventUtils.search(mEventArray, detectKind, origin) != null) {
response.putExtra(DConnectMessage.EXTRA_RESULT, DConnectMessage.RESULT_OK);
mContext.sendResponse(response);
return;
}
// store event register info.
HumanDetectEvent event = new HumanDetectEvent(detectKind, origin, requestParams, response);
mEventArray.add(event);
// response(success)
response.putExtra(DConnectMessage.EXTRA_RESULT, DConnectMessage.RESULT_OK);
response.putExtra(DConnectMessage.EXTRA_VALUE, "Register OnDetection event");
mContext.sendResponse(response);
}
/**
* unregister detect event.
*
* @param detectKind detectKind
* @param origin origin
*/
public void unregisterDetectEvent(final HumanDetectKind detectKind, final String origin) {
if (DEBUG) {
Log.d(TAG, "unregisterDetectEvent() detectKind:" + detectKind.toString() + " origin:" + origin);
}
// remove event register info.
HumanDetectEventUtils.remove(mEventArray, detectKind, origin);
}
/**
* remove all detect event.
*/
public void removeAllDetectEvent() {
if (!mEventArray.isEmpty()) {
mEventArray.clear();
}
}
/**
* remove detect event.
* @param origin origin
*/
public void removeDetectEvent(final String origin) {
HumanDetectEventUtils.remove(mEventArray, origin);
}
/**
* Get serviceId from bluetoothAddress.
*
* @param address bluetoothAddress
* @return serviceId
*/
public static String getServiceId(final String address) {
return address.replace(":", "").toLowerCase(Locale.ENGLISH);
}
/**
* get event interval by detectKind and origin.
*
* @param detectKind detect kind
* @param origin session key
* @return not null: event interval / null: event not fuond
*/
public Long getEventInterval(final HumanDetectKind detectKind, final String origin) {
for (HumanDetectEvent event : mEventArray) {
if (event.getOrigin().equals(origin) && event.getKind() == detectKind) {
return event.getRequestParams().getEvent().getInterval();
}
}
return null;
}
/**
* check exist event by interval.
*
* @param interval interval.
* @return true: exist / false not exist
*/
public boolean checkExistEventByInterval(final long interval) {
for (HumanDetectEvent event : mEventArray) {
if (event.getRequestParams().getEvent().getInterval() == interval) {
return true;
}
}
return false;
}
/**
* get detection process.
* @param detectKind detectKind
* @param requestParams requestParams
* @param response response
*/
public void doGetDetectionProc(final HumanDetectKind detectKind, final HumanDetectRequestParams requestParams,
final Intent response) {
// check comm busy.
if (checkCommBusy()) {
if (DEBUG) {
Log.d(TAG, "doGetDetectionProc() - BUG: Supposed to have been checked in HvcDeviceService.");
}
MessageUtils.setIllegalDeviceStateError(response, "device busy.");
mContext.sendResponse(response);
return;
}
// create lister.
HvcDetectListener listener = new HvcDetectListener() {
@Override
public void onDetectFinished(final HVC_PRM hvcPrm, final HVC_RES hvcRes) {
if (DEBUG) {
Log.d(TAG, "<GET> detect finished. body:" + hvcRes.body.size() + " hand:" + hvcRes.hand.size()
+ " face:" + hvcRes.face.size());
HvcResponseUtils.debugLogHvcRes(hvcRes, TAG);
}
// send response.
HvcResponseUtils.setDetectResultResponse(response, requestParams, hvcRes, detectKind);
if (checkDetectResult(detectKind, response)) {
response.putExtra(DConnectMessage.EXTRA_RESULT, DConnectMessage.RESULT_OK);
} else {
MessageUtils.setUnknownError(response, "No data to be sent.");
}
mContext.sendResponse(response);
}
@Override
public void onSetParamError(final int status) {
if (DEBUG) {
Log.d(TAG, "<GET> set parameter error. status:" + status);
}
MessageUtils.setIllegalDeviceStateError(response, "set parameter error. status:" + status);
mContext.sendResponse(response);
}
@Override
public void onRequestDetectError(final int status) {
if (DEBUG) {
Log.d(TAG, "<GET> request detect error. status:" + status);
}
MessageUtils.setIllegalDeviceStateError(response, "request detect error. status:" + status);
mContext.sendResponse(response);
}
@Override
public void onDetectError(final int status) {
if (DEBUG) {
Log.d(TAG, "<GET> detect error. status:" + status);
}
MessageUtils.setIllegalDeviceStateError(response, "detect error. status:" + status);
mContext.sendResponse(response);
}
};
// detect request.
commRequestProc(requestParams, listener);
}
/**
* check comm busy.
* @return true: busy / false: not busy.
*/
public boolean checkCommBusy() {
return mHvcBle.getStatus() == HVC_BLE.STATE_BUSY;
}
/**
* check whether this manager has a device.
* @return true: this manager has a device / false: this manager has no device.
*/
public boolean checkConnect() {
int commStatus = mHvcBle.getCommStatus();
return commStatus != HVC.HVC_ERROR_NODEVICES;
}
/**
* check device connect.
* @return true: connected / false: disconnected.
*/
public boolean isConnected() {
return mHvcBle.getStatus() == HVC_BLE.STATE_CONNECTED;
}
/**
* on event process (send event, only events that interval matches.).
*
* @param interval interval. match interval
*/
public void onEventProc(final long interval) {
// comm busy.
if (checkCommBusy()) {
if (DEBUG) {
Log.d(TAG, "onEventProc() - skip event process.(busy)");
}
return;
}
// get request parameter by events.
final HumanDetectRequestParams requestParams = getRequestParamsByEvents(interval);
// create lister.
HvcDetectListener listener = new HvcDetectListener() {
@Override
public void onDetectFinished(final HVC_PRM hvcPrm, final HVC_RES hvcRes) {
if (DEBUG) {
Log.d(TAG, "<EVENT> detect finished. body:" + hvcRes.body.size() + " hand:" + hvcRes.hand.size()
+ " face:" + hvcRes.face.size());
}
// send event.
for (HumanDetectEvent humanDetectEvent : mEventArray) {
String attribute = HvcConvertUtils.convertToEventAttribute(humanDetectEvent.getKind());
HumanDetectKind detectKind = humanDetectEvent.getKind();
if (humanDetectEvent.getRequestParams().getEvent().getInterval() == requestParams
.getEvent().getInterval()) {
List<Event> events = EventManager.INSTANCE.getEventList(mServiceId,
HumanDetectionProfile.PROFILE_NAME, null, attribute);
for (Event event : events) {
Intent intent = EventManager.createEventMessage(event);
HvcResponseUtils.setDetectResultResponse(intent, requestParams, hvcRes, detectKind);
if (!checkDetectResult(detectKind, intent)) {
continue;
}
mContext.sendEvent(intent, event.getAccessToken());
if (DEBUG) {
Log.d(TAG, "<EVENT> send event. attribute:" + attribute);
}
}
}
}
}
@Override
public void onSetParamError(final int status) {
if (DEBUG) {
Log.d(TAG, "<EVENT> set parameter error. status:" + status);
}
}
@Override
public void onRequestDetectError(final int status) {
if (DEBUG) {
Log.d(TAG, "<EVENT> request detect error. status:" + status);
}
}
@Override
public void onDetectError(final int status) {
if (DEBUG) {
Log.d(TAG, "<EVENT> detect error. status:" + status);
}
}
};
// request.
commRequestProc(requestParams, listener);
}
/**
* Checks whether the specified intent has a necessary bundle or not.
*
* @param detectKind a kind of detection
* @param intent an instance of {@link Intent}
* @return <code>true</code> if the specified intent has a necessary bundle, otherwise <code>false</code>
*/
private boolean checkDetectResult(final HumanDetectKind detectKind, final Intent intent) {
switch (detectKind) {
case BODY:
return intent.hasExtra(HumanDetectionProfile.PARAM_BODYDETECTS);
case HAND:
return intent.hasExtra(HumanDetectionProfile.PARAM_HANDDETECTS);
case FACE:
return intent.hasExtra(HumanDetectionProfile.PARAM_FACEDETECTS);
case HUMAN:
return intent.hasExtra("humanDetect");
default:
return false;
}
}
/**
* timeout judge process.
*/
public void onTimeoutJudgeProc() {
// BLE connect timeout judge.
if (checkConnect()
&& (System.currentTimeMillis() - mLastAccessTime) > HvcConstants.HVC_CONNECT_TIMEOUT_TIME) {
if (DEBUG) {
Log.d(TAG,
"disconnect(BLE connect timeout). Not been accessed more than "
+ (HvcConstants.HVC_CONNECT_TIMEOUT_TIME / 1000) + " seconds");
}
mHvcBle.disconnect();
}
}
/**
* get request parameter by events.
* @param interval interval[msec]
* @return request parameter by events.
*/
private HumanDetectRequestParams getRequestParamsByEvents(final long interval) {
HumanDetectRequestParams requestParams = new HumanDetectRequestParams();
requestParams.setEvent(HvcDetectRequestParams.getDefaultEventRequestParameter());
for (HumanDetectEvent event : mEventArray) {
if (event.getRequestParams().getEvent().getInterval() == interval) {
if (event.getKind() == HumanDetectKind.BODY) {
requestParams.setBody(event.getRequestParams().getBody());
} else if (event.getKind() == HumanDetectKind.HAND) {
requestParams.setHand(event.getRequestParams().getHand());
} else if (event.getKind() == HumanDetectKind.FACE) {
requestParams.setFace(event.getRequestParams().getFace());
} else if (event.getKind() == HumanDetectKind.HUMAN) {
requestParams.setBody(event.getRequestParams().getBody());
requestParams.setHand(event.getRequestParams().getHand());
requestParams.setFace(event.getRequestParams().getFace());
} else {
if (DEBUG) {
Log.d(TAG, "invalid event.getKind()" + event.getKind().ordinal());
}
}
requestParams.setEvent(event.getRequestParams().getEvent());
}
}
return requestParams;
}
/**
* HVC comm request process.
* @param requestParams request parameters
* @param listener callback listener
*/
private void commRequestProc(final HumanDetectRequestParams requestParams, final HvcDetectListener listener) {
if (checkCommBusy()) {
if (DEBUG) {
Log.d(TAG, "commRequestProc() - BUG: Supposed to have been checked in HvcDeviceService.");
}
return;
}
mRequestParams = requestParams;
mHvcPrm = new HvcDetectRequestParams(requestParams).getHvcParams();
mListener = listener;
// Replace last access time.
mLastAccessTime = System.currentTimeMillis();
if (DEBUG) {
Log.d(TAG, "commRequestProc() - mLastAccessTime:" + mLastAccessTime);
}
// BLE initialize (GATT)
mHvcBle.setCallBack(new HVCBleCallback() {
@Override
public void onConnected() {
super.onConnected();
if (DEBUG) {
Log.d(TAG, "commRequestProc() - onConnected()");
}
// send parameter(if cache hit, no send)
sendParameterProc();
}
@Override
public void onDisconnected() {
if (DEBUG) {
Log.d(TAG, "commRequestProc() - onDisconnected()");
}
super.onDisconnected();
}
@Override
public void onPostSetParam(final int nRet, final byte outStatus) {
super.onPostSetParam(nRet, outStatus);
if (DEBUG) {
Log.d(TAG, "commRequestProc() - onPostSetParam()");
}
// replace cache.
mCacheHvcPrm = mHvcPrm;
// send detect request.
sendDetectRequestProc();
}
@Override
public void onPostExecute(final int nRet, final byte outStatus) {
if (DEBUG) {
Log.d(TAG, "commRequestProc() - onPostExecute() nRet:" + nRet + " outStatus:" + outStatus);
}
if (nRet != HVC.HVC_NORMAL || outStatus != 0) {
// Error processing
mListener.onDetectError(nRet);
} else {
mListener.onDetectFinished(mHvcPrm, mHvcRes);
}
}
});
// connect
int commStatus = mHvcBle.getCommStatus();
if (commStatus == HVC.HVC_ERROR_NODEVICES || commStatus == HVC.HVC_ERROR_DISCONNECTED) {
if (commStatus == HVC.HVC_ERROR_DISCONNECTED) {
mHvcBle.disconnect();
try {
/* Disconnect wait. */
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// clear cache.
mCacheHvcPrm = null;
// connect
mHvcBle.connect(mContext, mBluetoothDevice);
try {
/* Connect wait. */
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// already connect
sendParameterProc();
}
}
/**
* send parameter(if cache hit, no send).
*/
private void sendParameterProc() {
// send parameter.
if (mCacheHvcPrm == null || !mCacheHvcPrm.equals(mHvcPrm)) {
// no hit cache, send parameter.
if (DEBUG) {
Log.d(TAG, "mHvcBle.setParam()");
}
int result = mHvcBle.setParam(mHvcPrm);
if (result != HVC.HVC_NORMAL) {
mListener.onSetParamError(result);
}
} else {
// cache hit (no send parameter, next step)
sendDetectRequestProc();
}
}
/**
* send detect request.
*/
private void sendDetectRequestProc() {
// send detect request.
int useFunc = (new HvcDetectRequestParams(mRequestParams)).getUseFunc();
if (DEBUG) {
Log.d(TAG, "mHvcBle.execute() useFunc:" + useFunc);
}
int result = mHvcBle.execute(useFunc, mHvcRes);
if (result != HVC.HVC_NORMAL) {
mListener.onRequestDetectError(result);
}
}
}