/* Copyright (C) 2024 Damien Gaignon, Martin.JM This file is part of Gadgetbridge. Gadgetbridge is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gadgetbridge 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei; import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import android.media.AudioManager; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalTime; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.CameraActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.App; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Calls; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.CameraRemote; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.DeviceConfig; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Ephemeris; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.EphemerisFileUpload; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FindPhone; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.GpsAndTime; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Menstrual; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.P2P; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Watchface; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Weather; import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetBatteryLevelRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Request; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetPhoneInfoRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFileUploadComplete; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendGpsStatusRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendMenstrualModifyTimeRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFileUploadAck; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFileUploadChunk; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendFileUploadHash; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWatchfaceConfirm; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendWatchfaceOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SetMusicStatusRequest; import nodomain.freeyourgadget.gadgetbridge.util.GB; /** * Handles responses that are not a reply to a request */ public class AsynchronousResponse { private static final Logger LOG = LoggerFactory.getLogger(AsynchronousResponse.class); private final HuaweiSupportProvider support; private final Handler mFindPhoneHandler = new Handler(); private final static HashMap dayOfWeekMap = new HashMap<>(); static { dayOfWeekMap.put(Calendar.MONDAY, DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_MO); dayOfWeekMap.put(Calendar.TUESDAY, DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_TU); dayOfWeekMap.put(Calendar.WEDNESDAY, DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_WE); dayOfWeekMap.put(Calendar.THURSDAY, DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_TH); dayOfWeekMap.put(Calendar.FRIDAY, DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_FR); dayOfWeekMap.put(Calendar.SATURDAY, DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_SA); dayOfWeekMap.put(Calendar.SUNDAY, DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_SU); } public AsynchronousResponse(HuaweiSupportProvider support) { this.support = support; } public void handleResponse(HuaweiPacket response) { // Ignore messages if the key isn't set yet if (support.getParamsProvider().getSecretKey() == null) return; try { response.parseTlv(); } catch (HuaweiPacket.ParseException e) { LOG.error("Parse TLV exception", e); return; } try { handleFindPhone(response); handleMusicControls(response); handleCallControls(response); handlePhoneInfo(response); handleMenstrualModifyTime(response); handleWeatherCheck(response); handleGpsRequest(response); handleFileUpload(response); handleWatchface(response); handleCameraRemote(response); handleApp(response); handleP2p(response); handleEphemeris(response); handleEphemerisUploadService(response); handleAsyncBattery(response); } catch (Request.ResponseParseException e) { LOG.error("Response parse exception", e); } } private void handleFindPhone(HuaweiPacket response) throws Request.ResponseParseException { if (response.serviceId == FindPhone.id && response.commandId == FindPhone.Response.id) { if (!(response instanceof FindPhone.Response)) throw new Request.ResponseTypeMismatchException(response, FindPhone.Response.class); SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(support.getDeviceMac()); String findPhone = sharedPreferences.getString(DeviceSettingsPreferenceConst.PREF_FIND_PHONE, support.getContext().getString(R.string.p_off)); if (findPhone.equals(support.getContext().getString(R.string.p_off))) { LOG.debug("Find phone command received, but it is disabled"); // TODO: hide applet on device return; } if (sharedPreferences.getBoolean("disable_find_phone_with_dnd", false) && dndActive()) { LOG.debug("Find phone command received, ringing prevented because of DND"); // TODO: stop the band from showing as ringing return; } if (!findPhone.equals(support.getContext().getString(R.string.p_on))) { // Duration set, stop after specified time String strDuration = sharedPreferences.getString(DeviceSettingsPreferenceConst.PREF_FIND_PHONE_DURATION, "0"); int duration = Integer.parseInt(strDuration); if (duration > 0) { mFindPhoneHandler.postDelayed(new Runnable() { @Override public void run() { GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone(); findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP; support.evaluateGBDeviceEvent(findPhoneEvent); // TODO: stop the band from showing as ringing } }, duration * 1000L); } } GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone(); if (((FindPhone.Response) response).start) findPhoneEvent.event = GBDeviceEventFindPhone.Event.START; else findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP; support.evaluateGBDeviceEvent(findPhoneEvent); } } private boolean dndActive() { SharedPreferences sharedPreferences = GBApplication.getDeviceSpecificSharedPrefs(support.getDeviceMac()); String dndSwitch = sharedPreferences.getString(DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB, "off"); if (dndSwitch.equals(DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_OFF)) return false; String startStr = sharedPreferences.getString(DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_START, "00:00"); if (dndSwitch.equals("automatic")) startStr = "00:00"; String endStr = sharedPreferences.getString(DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_END, "23:59"); if (dndSwitch.equals("automatic")) endStr = "23:59"; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { LocalTime currentTime = LocalTime.now(); LocalTime start = LocalTime.parse(startStr); LocalTime end = LocalTime.parse(endStr); if (start.isAfter(currentTime)) return false; if (end.isBefore(currentTime)) return false; } else { @SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm"); try { Date currentTime = dateFormat.parse(String.format(GBApplication.getLanguage(), "%d:%d", Calendar.getInstance().get(Calendar.HOUR_OF_DAY), Calendar.getInstance().get(Calendar.MINUTE))); Date start = dateFormat.parse(startStr); Date end = dateFormat.parse(endStr); assert start != null; if (start.after(currentTime)) return false; assert end != null; if (end.before(currentTime)) return false; } catch (ParseException e) { LOG.error("Parse exception for DnD", e); } } Calendar date = Calendar.getInstance(); String preferenceString = dayOfWeekMap.get(date.get(Calendar.DAY_OF_WEEK)); return sharedPreferences.getBoolean(preferenceString, true); } /** * Handles asynchronous music packet, for the following events: * - The app is opened on the band (sends back music info) * - A button is clicked * - Play * - Pause * - Previous * - Next * - The volume is adjusted * * @param response Packet to be handled */ private void handleMusicControls(HuaweiPacket response) throws Request.ResponseParseException { if (response.serviceId == MusicControl.id) { AudioManager audioManager = (AudioManager) this.support.getContext().getSystemService(Context.AUDIO_SERVICE); if (response.commandId == MusicControl.MusicStatusResponse.id) { if (!(response instanceof MusicControl.MusicStatusResponse)) throw new Request.ResponseTypeMismatchException(response, MusicControl.MusicStatusResponse.class); MusicControl.MusicStatusResponse resp = (MusicControl.MusicStatusResponse) response; if (resp.status != -1 && resp.status != 0x000186A0) { LOG.warn("Music information error, will stop here: " + Integer.toHexString(resp.status)); return; } LOG.debug("Music information requested, sending acknowledgement and music info."); SetMusicStatusRequest setMusicStatusRequest = new SetMusicStatusRequest(this.support, MusicControl.MusicStatusResponse.id, MusicControl.successValue); try { setMusicStatusRequest.doPerform(); } catch (IOException e) { GB.toast("Failed to send music status request", Toast.LENGTH_SHORT, GB.ERROR, e); LOG.error("Failed to send music status request (1)", e); } // Update and send Music Info this.support.refreshMediaManager(); this.support.sendSetMusic(); } else if (response.commandId == MusicControl.Control.id) { if (!(response instanceof MusicControl.Control.Response)) throw new Request.ResponseTypeMismatchException(response, MusicControl.Control.Response.class); MusicControl.Control.Response resp = (MusicControl.Control.Response) response; if (resp.buttonPresent) { if (resp.button != MusicControl.Control.Response.Button.Unknown) { GBDeviceEventMusicControl musicControl = new GBDeviceEventMusicControl(); switch (resp.button) { case Play: LOG.debug("Music - Play button event received"); musicControl.event = GBDeviceEventMusicControl.Event.PLAY; break; case Pause: LOG.debug("Music - Pause button event received"); musicControl.event = GBDeviceEventMusicControl.Event.PAUSE; break; case Previous: LOG.debug("Music - Previous button event received"); musicControl.event = GBDeviceEventMusicControl.Event.PREVIOUS; break; case Next: LOG.debug("Music - Next button event received"); musicControl.event = GBDeviceEventMusicControl.Event.NEXT; break; case Volume_up: LOG.debug("Music - Volume up button event received"); musicControl.event = GBDeviceEventMusicControl.Event.VOLUMEUP; break; case Volume_down: LOG.debug("Music - Volume down button event received"); musicControl.event = GBDeviceEventMusicControl.Event.VOLUMEDOWN; break; default: } this.support.evaluateGBDeviceEvent(musicControl); } } if (resp.volumePresent) { byte volume = resp.volume; if (volume > audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) { LOG.warn("Music - Received volume is too high: 0x" + Integer.toHexString(volume) + " > 0x" + Integer.toHexString(audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) ); // TODO: probably best to send back an error code, though I wouldn't know which return; } if (Build.VERSION.SDK_INT > 28) { if (volume < audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC)) { LOG.warn("Music - Received volume is too low: 0x" + Integer.toHexString(volume) + " < 0x" + audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC) ); // TODO: probably best to send back an error code, though I wouldn't know which return; } } LOG.debug("Music - Setting volume to: 0x" + Integer.toHexString(volume)); audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); } if (resp.buttonPresent || resp.volumePresent) { SetMusicStatusRequest setMusicStatusRequest = new SetMusicStatusRequest(this.support, MusicControl.Control.id, MusicControl.successValue); try { setMusicStatusRequest.doPerform(); } catch (IOException e) { GB.toast("Failed to send music status request", Toast.LENGTH_SHORT, GB.ERROR, e); LOG.error("Failed to send music status request (2)", e); } // Delay so the media app has a moment to change state new Handler(Looper.getMainLooper()).postDelayed(() -> { // Update and send Music Info this.support.refreshMediaManager(); this.support.sendSetMusic(); }, 100); } } else if (response.commandId == MusicControl.UploadMusicFileInfo.id) { if (!(response instanceof MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest)) throw new Request.ResponseTypeMismatchException(response, MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest.class); MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest resp = (MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest) response; support.getHuaweiMusicManager().uploadMusicInfo(resp.songIndex, resp.songFileName); } } } private void handleCallControls(HuaweiPacket response) throws Request.ResponseParseException { if (response.serviceId == Calls.id && response.commandId == Calls.AnswerCallResponse.id) { if (!(response instanceof Calls.AnswerCallResponse)) throw new Request.ResponseTypeMismatchException(response, Calls.AnswerCallResponse.class); SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(support.getDevice().getAddress()); GBDeviceEventCallControl callControlEvent = new GBDeviceEventCallControl(); switch (((Calls.AnswerCallResponse) response).action) { case UNKNOWN: LOG.info("Unknown action for call"); return; case CALL_ACCEPT: callControlEvent.event = GBDeviceEventCallControl.Event.ACCEPT; LOG.info("Accepted call"); if (!prefs.getBoolean("enable_call_accept", true)) { LOG.info("Disabled accepting calls, ignoring"); return; } break; case CALL_REJECT: callControlEvent.event = GBDeviceEventCallControl.Event.REJECT; LOG.info("Rejected call"); if (!prefs.getBoolean("enable_call_reject", true)) { LOG.info("Disabled rejecting calls, ignoring"); return; } break; } support.evaluateGBDeviceEvent(callControlEvent); } } private void handlePhoneInfo(HuaweiPacket response) { if (response.serviceId == DeviceConfig.id && response.commandId == DeviceConfig.PhoneInfo.id) { if (!(response instanceof DeviceConfig.PhoneInfo.Response)) { // TODO: exception return; } DeviceConfig.PhoneInfo.Response phoneInfoResp = (DeviceConfig.PhoneInfo.Response) response; if (phoneInfoResp.isAck) { LOG.info("Not responding to ack for PhoneInfo"); return; } GetPhoneInfoRequest getPhoneInfoReq = new GetPhoneInfoRequest(this.support, phoneInfoResp.info); try { getPhoneInfoReq.doPerform(); } catch (IOException e) { e.printStackTrace(); } } } private void handleMenstrualModifyTime(HuaweiPacket response) { if (response.serviceId == Menstrual.id && response.commandId == Menstrual.ModifyTime.id) { if (!(response instanceof Menstrual.ModifyTime.Response)) { // TODO: exception return; } //Menstrual.ModifyTime.Response menstrualModifyTimeResp = (Menstrual.ModifyTime.Response) response; SendMenstrualModifyTimeRequest sendMenstrualModifyTimeReq = new SendMenstrualModifyTimeRequest(this.support); try { sendMenstrualModifyTimeReq.doPerform(); } catch (IOException e) { e.printStackTrace(); } } } private void handleFileUpload(HuaweiPacket response) throws Request.ResponseParseException { if (response.serviceId == FileUpload.id) { if (response.commandId == FileUpload.FileInfoSend.id) { if (!(response instanceof FileUpload.FileInfoSend.Response)) throw new Request.ResponseTypeMismatchException(response, FileUpload.FileInfoSend.Response.class); if (support.huaweiUploadManager.getFileUploadInfo() == null) { LOG.error("Upload file info received but no file to upload"); } else { FileUpload.FileInfoSend.Response resp = (FileUpload.FileInfoSend.Response) response; if (resp.result != 100000) { if (support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback() != null) { support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback().onError(resp.result); } else { LOG.error("Upload file info error without callback: {}", resp.result); } //Cleanup support.huaweiUploadManager.setFileUploadInfo(null); } } } else if (response.commandId == FileUpload.FileHashSend.id) { if (!(response instanceof FileUpload.FileHashSend.Response)) throw new Request.ResponseTypeMismatchException(response, FileUpload.FileHashSend.Response.class); if (support.huaweiUploadManager.getFileUploadInfo() == null) { LOG.error("Upload file hash requested but no file to upload"); } else { FileUpload.FileHashSend.Response resp = (FileUpload.FileHashSend.Response) response; support.huaweiUploadManager.getFileUploadInfo().setFileId(resp.fileId); try { SendFileUploadHash sendFileUploadHash = new SendFileUploadHash(support, support.huaweiUploadManager); sendFileUploadHash.doPerform(); } catch (IOException e) { LOG.error("Could not send file upload hash request", e); } } } else if (response.commandId == FileUpload.FileUploadConsultAck.id) { if (!(response instanceof FileUpload.FileUploadConsultAck.Response)) throw new Request.ResponseTypeMismatchException(response, FileUpload.FileUploadConsultAck.Response.class); if (support.huaweiUploadManager.getFileUploadInfo() == null) { LOG.error("Upload file ask requested but no file to upload"); } else { FileUpload.FileUploadConsultAck.Response resp = (FileUpload.FileUploadConsultAck.Response) response; support.huaweiUploadManager.getFileUploadInfo().setFileUploadParams(resp.fileUploadParams); try { if (support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback() != null) { support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback().onUploadStart(); } SendFileUploadAck sendFileUploadAck = new SendFileUploadAck(support, resp.fileUploadParams.no_encrypt, support.huaweiUploadManager.getFileUploadInfo().getFileId()); sendFileUploadAck.doPerform(); } catch (IOException e) { LOG.error("Could not send file upload ack request", e); } } } else if (response.commandId == FileUpload.FileNextChunkParams.id) { if (!(response instanceof FileUpload.FileNextChunkParams)) throw new Request.ResponseTypeMismatchException(response, FileUpload.FileNextChunkParams.class); if (support.huaweiUploadManager.getFileUploadInfo() == null) { LOG.error("Upload file next chunk requested but no file to upload"); } else { FileUpload.FileNextChunkParams resp = (FileUpload.FileNextChunkParams) response; support.huaweiUploadManager.getFileUploadInfo().setUploadChunkSize(resp.nextchunkSize); support.huaweiUploadManager.getFileUploadInfo().setCurrentUploadPosition(resp.bytesUploaded); int progress = Math.round(((float) resp.bytesUploaded / (float) support.huaweiUploadManager.getFileUploadInfo().getFileSize()) * 100); try { if (support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback() != null) { support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback().onUploadProgress(progress); } SendFileUploadChunk sendFileUploadChunk = new SendFileUploadChunk(support, support.huaweiUploadManager); sendFileUploadChunk.doPerform(); } catch (IOException e) { LOG.error("Could not send fileupload next chunk request", e); } } } else if (response.commandId == FileUpload.FileUploadResult.id) { if (support.huaweiUploadManager.getFileUploadInfo() == null) { LOG.error("Upload file result requested but no file to upload"); } else { try { byte fileId = support.huaweiUploadManager.getFileUploadInfo().getFileId(); if (support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback() != null) { support.huaweiUploadManager.getFileUploadInfo().getFileUploadCallback().onUploadComplete(); } //Cleanup support.huaweiUploadManager.setFileUploadInfo(null); SendFileUploadComplete sendFileUploadComplete = new SendFileUploadComplete(this.support, fileId); sendFileUploadComplete.doPerform(); } catch (IOException e) { LOG.error("Could not send file upload result request", e); } } } } } private void handleWatchface(HuaweiPacket response) throws Request.ResponseParseException { if (response.serviceId == Watchface.id) { if (response.commandId == Watchface.WatchfaceConfirm.id) { try { if (!(response instanceof Watchface.WatchfaceConfirm.Response)) throw new Request.ResponseTypeMismatchException(response, Watchface.WatchfaceConfirm.class); Watchface.WatchfaceConfirm.Response resp = (Watchface.WatchfaceConfirm.Response) response; SendWatchfaceConfirm sendWatchfaceConfirm = new SendWatchfaceConfirm(this.support, resp.fileName); sendWatchfaceConfirm.doPerform(); if (resp.reportType == 0x02) { //make uploaded watchface active SendWatchfaceOperation sendWatchfaceOperation = new SendWatchfaceOperation(this.support, resp.fileName, Watchface.WatchfaceOperation.operationActive); sendWatchfaceOperation.doPerform(); } } catch (IOException e) { LOG.error("Could not send watchface confirm request", e); } } } } private void handleApp(HuaweiPacket response) throws Request.ResponseParseException { if (response.serviceId == App.id) { if (response.commandId == 0x2) { try { byte status = response.getTlv().getByte(0x1); if (status == (byte) 0x66 || status == (byte) 0x69) { this.support.getHuaweiAppManager().requestAppList(); } } catch (HuaweiPacket.MissingTagException e) { LOG.error("Could not send watchface confirm request", e); } } } } private void handleP2p(HuaweiPacket response) throws Request.ResponseParseException { if (response.serviceId == P2P.id && response.commandId == P2P.P2PCommand.id) { if (!(response instanceof P2P.P2PCommand.Response)) throw new Request.ResponseTypeMismatchException(response, P2P.P2PCommand.class); try { this.support.getHuaweiP2PManager().handlePacket((P2P.P2PCommand.Response) response); } catch (Exception e) { LOG.error("Error in P2P service", e); } } } private void handleWeatherCheck(HuaweiPacket response) { if (response.serviceId == Weather.id && response.commandId == 0x04) { support.huaweiWeatherManager.handleAsyncMessage(response); } } private void handleGpsRequest(HuaweiPacket response) { if (response.serviceId == GpsAndTime.id && response.commandId == GpsAndTime.GpsStatus.id) { if (!(response instanceof GpsAndTime.GpsStatus.Response)) { // TODO: exception? return; } // Send back OK try { SendGpsStatusRequest sendGpsStatusRequest = new SendGpsStatusRequest(this.support); sendGpsStatusRequest.doPerform(); } catch (IOException e) { LOG.error("Could not send back OK"); } support.setGps(((GpsAndTime.GpsStatus.Response) response).enableGps); } } private void handleEphemeris(HuaweiPacket response) { if (response.serviceId == Ephemeris.id && response.commandId == Ephemeris.OperatorData.id) { if (!(response instanceof Ephemeris.OperatorData.OperatorIncomingRequest)) { return; } byte operationInfo = ((Ephemeris.OperatorData.OperatorIncomingRequest) response).operationInfo; int operationTime = ((Ephemeris.OperatorData.OperatorIncomingRequest) response).operationTime; LOG.info("Ephemeris: operation: {} time: {}", operationInfo, operationTime); support.getHuaweiEphemerisManager().handleOperatorRequest(operationInfo, operationTime); } } private void handleEphemerisUploadService(HuaweiPacket response) { if (response.serviceId == EphemerisFileUpload.id) { if (response.commandId == EphemerisFileUpload.FileList.id) { if (!(response instanceof EphemerisFileUpload.FileList.FileListIncomingRequest)) { return; } support.getHuaweiEphemerisManager().handleFileSendRequest(((EphemerisFileUpload.FileList.FileListIncomingRequest) response).fileType, ((EphemerisFileUpload.FileList.FileListIncomingRequest) response).productId); } else if (response.commandId == EphemerisFileUpload.FileConsult.id) { if (!(response instanceof EphemerisFileUpload.FileConsult.FileConsultIncomingRequest)) { return; } EphemerisFileUpload.FileConsult.FileConsultIncomingRequest res = (EphemerisFileUpload.FileConsult.FileConsultIncomingRequest) response; support.getHuaweiEphemerisManager().handleFileConsultIncomingRequest(res.responseCode, res.protocolVersion, res.bitmapEnable, res.transferSize, res.maxDataSize, res.timeOut, res.fileType); } else if (response.commandId == EphemerisFileUpload.QuerySingleFileInfo.id) { if (!(response instanceof EphemerisFileUpload.QuerySingleFileInfo.QuerySingleFileInfoIncomingRequest)) { return; } support.getHuaweiEphemerisManager().handleSingleFileIncomingRequest(((EphemerisFileUpload.QuerySingleFileInfo.QuerySingleFileInfoIncomingRequest) response).fileName); } else if (response.commandId == EphemerisFileUpload.DataRequest.id) { if (!(response instanceof EphemerisFileUpload.DataRequest.DataRequestIncomingRequest)) { return; } EphemerisFileUpload.DataRequest.DataRequestIncomingRequest res = (EphemerisFileUpload.DataRequest.DataRequestIncomingRequest) response; support.getHuaweiEphemerisManager().handleDataRequestIncomingRequest(res.responseCode, res.fileName, res.offset, res.len, res.bitmap); } else if (response.commandId == EphemerisFileUpload.UploadData.id) { if (!(response instanceof EphemerisFileUpload.UploadData.UploadDataResponse)) { return; } support.getHuaweiEphemerisManager().handleFileUploadResponse(((EphemerisFileUpload.UploadData.UploadDataResponse) response).responseCode); } else if (response.commandId == EphemerisFileUpload.UploadDone.id) { if (!(response instanceof EphemerisFileUpload.UploadDone.UploadDoneIncomingRequest)) { return; } support.getHuaweiEphemerisManager().handleFileDoneRequest(((EphemerisFileUpload.UploadDone.UploadDoneIncomingRequest) response).uploadResult); } } } private void handleCameraRemote(HuaweiPacket response) { if (response.serviceId == CameraRemote.id && response.commandId == CameraRemote.CameraRemoteStatus.id) { if (!(response instanceof CameraRemote.CameraRemoteStatus.Response)) { // TODO: exception? return; } if (!CameraActivity.supportsCamera()) { LOG.error("No camera present"); // TODO: Toast? return; } switch (((CameraRemote.CameraRemoteStatus.Response) response).event) { case OPEN_CAMERA: GBDeviceEventCameraRemote openCameraEvent = new GBDeviceEventCameraRemote(); openCameraEvent.event = GBDeviceEventCameraRemote.Event.OPEN_CAMERA; support.evaluateGBDeviceEvent(openCameraEvent); break; case TAKE_PICTURE: GBDeviceEventCameraRemote takePictureEvent = new GBDeviceEventCameraRemote(); takePictureEvent.event = GBDeviceEventCameraRemote.Event.TAKE_PICTURE; support.evaluateGBDeviceEvent(takePictureEvent); break; case CLOSE_CAMERA: GBDeviceEventCameraRemote closeCameraEvent = new GBDeviceEventCameraRemote(); closeCameraEvent.event = GBDeviceEventCameraRemote.Event.CLOSE_CAMERA; support.evaluateGBDeviceEvent(closeCameraEvent); break; } } } private void handleAsyncBattery(HuaweiPacket response) { if (response.serviceId == DeviceConfig.id && response.commandId == DeviceConfig.BatteryLevel.id_change) { if (!(response instanceof DeviceConfig.BatteryLevel.Response)) { // TODO: exception? return; } DeviceConfig.BatteryLevel.Response resp = (DeviceConfig.BatteryLevel.Response) response; if (resp.multi_level == null) { byte batteryLevel = resp.level; this.support.getDevice().setBatteryLevel(batteryLevel); GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo(); batteryInfo.state = BatteryState.BATTERY_NORMAL; batteryInfo.level = (int) batteryLevel & 0xff; this.support.evaluateGBDeviceEvent(batteryInfo); } else { // Handle multiple batteries for (int i = 0; i < resp.multi_level.length; i++) { int level = (int) resp.multi_level[i] & 0xff; this.support.getDevice().setBatteryLevel(level, i); GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo(); batteryInfo.batteryIndex = i; batteryInfo.state = resp.status != null && resp.status.length > i ? GetBatteryLevelRequest.byteToBatteryState(resp.status[i]) : BatteryState.BATTERY_NORMAL; batteryInfo.level = level; this.support.evaluateGBDeviceEvent(batteryInfo); } } if (GBApplication.getDevicePrefs(this.support.getDevice()).getBatteryPollingEnabled()) { if (!this.support.startBatteryRunnerDelayed()) { GB.toast(this.support.getContext(), R.string.battery_polling_failed_start, Toast.LENGTH_SHORT, GB.ERROR); LOG.error("Failed to start the battery polling"); } } } } }