导航菜单

页面标题

页面副标题

xDrip+ v04633772025.07.16 - MedtrumCollectionService.java 源代码

正在查看: xDrip+ v04633772025.07.16 应用的 MedtrumCollectionService.java JAVA 源代码文件

本页面展示 JAVA 反编译生成的源代码文件,支持语法高亮显示。 仅供安全研究与技术分析使用,严禁用于任何非法用途。请遵守相关法律法规。


package com.eveningoutpost.dexdrip.cgm.medtrum;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.PowerManager;
import android.text.SpannableString;
import android.util.Pair;
import androidx.appcompat.R$styleable;
import com.eveningoutpost.dexdrip.Home;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.AnnexARx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.AuthRx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.AuthTx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.BackFillRx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.BackFillTx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.BaseMessage;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.CalibrateRx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.CalibrateTx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.ConnParamRx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.ConnParamTx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.InboundStream;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.StatusRx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.StatusTx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.TimeRx;
import com.eveningoutpost.dexdrip.cgm.medtrum.messages.TimeTx;
import com.eveningoutpost.dexdrip.importedlibraries.usbserial.driver.UsbId;
import com.eveningoutpost.dexdrip.importedlibraries.usbserial.util.HexDump;
import com.eveningoutpost.dexdrip.models.ActiveBluetoothDevice;
import com.eveningoutpost.dexdrip.models.BgReading;
import com.eveningoutpost.dexdrip.models.JoH;
import com.eveningoutpost.dexdrip.models.Prediction;
import com.eveningoutpost.dexdrip.models.TransmitterData;
import com.eveningoutpost.dexdrip.models.UserError;
import com.eveningoutpost.dexdrip.services.JamBaseBluetoothService;
import com.eveningoutpost.dexdrip.ui.helpers.Span;
import com.eveningoutpost.dexdrip.utilitymodels.BgGraphBuilder;
import com.eveningoutpost.dexdrip.utilitymodels.Inevitable;
import com.eveningoutpost.dexdrip.utilitymodels.PersistentStore;
import com.eveningoutpost.dexdrip.utilitymodels.Pref;
import com.eveningoutpost.dexdrip.utilitymodels.RxBleProvider;
import com.eveningoutpost.dexdrip.utilitymodels.StatusItem;
import com.eveningoutpost.dexdrip.utils.BtCallBack;
import com.eveningoutpost.dexdrip.utils.DexCollectionType;
import com.eveningoutpost.dexdrip.utils.DisconnectReceiver;
import com.eveningoutpost.dexdrip.utils.bt.Subscription;
import com.eveningoutpost.dexdrip.utils.framework.WakeLockTrampoline;
import com.eveningoutpost.dexdrip.xdrip;
import com.polidea.rxandroidble2.RxBleClient;
import com.polidea.rxandroidble2.RxBleConnection;
import com.polidea.rxandroidble2.RxBleDevice;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import java.security.InvalidAlgorithmParameterException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class MedtrumCollectionService extends JamBaseBluetoothService implements BtCallBack {
    private static final String STATIC_TAG = "MedtrumCollectionService";
    private static volatile String address = "";
    private static volatile Subscription connectionSubscription = null;
    private static long failover_time = 0;
    private static volatile Subscription indicationSubscription = null;
    public static volatile String lastErrorState = "";
    public static volatile String lastState = "Not running";
    private static long last_wake_up_time;
    private static volatile Subscription notificationSubscription;
    private static long retry_time;
    private static Scanner scanner;
    private static long serial;
    private static PendingIntent serviceFailoverIntent;
    private static PendingIntent serviceIntent;
    private static volatile Subscription stateSubscription;
    private volatile RxBleDevice bleDevice;
    private volatile RxBleConnection connection;
    private static volatile STATE state = STATE.INIT;
    private static volatile STATE last_automata_state = STATE.CLOSED;
    private static volatile boolean listen_connected = false;
    private static long retry_backoff = 0;
    private static long lastRecordTime = -1;
    private static volatile long lastInteractionTime = -1;
    private static int requestedBackfillSize = 0;
    private static AnnexARx lastAnnex = null;
    private static long wakeup_time = 0;
    private static long wakeup_jitter = 0;
    private static long max_wakeup_jitter = 0;
    protected String TAG = getClass().getSimpleName();
    private final RxBleClient rxBleClient = RxBleProvider.getSingleton();

    public static ObservableSource lambda$enable_features_and_listen$1(Observable observable) throws Exception {
        return observable;
    }

    public static ObservableSource lambda$enable_features_and_listen$5(Observable observable) throws Exception {
        return observable;
    }

    public enum STATE {
        INIT("Initializing"),
        SCAN("Scanning"),
        CONNECT("Waiting connect"),
        ENABLE("Enabling"),
        DISCOVER("Examining"),
        SET_TIME("Setting Time"),
        SET_CONN_PARAM("Setting Parameters"),
        CALIBRATE("Check Calibration"),
        LISTEN("Listening"),
        GET_DATA("Getting Data"),
        CLOSE("Sleeping"),
        CLOSED("Deep Sleeping");

        private static List<STATE> sequence;
        private final String str;

        static {
            STATE state = ENABLE;
            STATE state2 = SET_TIME;
            STATE state3 = SET_CONN_PARAM;
            STATE state4 = CALIBRATE;
            STATE state5 = LISTEN;
            STATE state6 = GET_DATA;
            ArrayList arrayList = new ArrayList();
            sequence = arrayList;
            arrayList.add(state);
            sequence.add(state6);
            sequence.add(state2);
            sequence.add(state3);
            sequence.add(state4);
            sequence.add(state5);
        }

        STATE(String str) {
            this.str = str;
        }

        public String getString() {
            return this.str;
        }

        public STATE next() {
            try {
                List<STATE> list = sequence;
                return list.get(list.indexOf(this) + 1);
            } catch (Exception unused) {
                return LISTEN;
            }
        }
    }

    @Override
    public synchronized boolean automata() {
        PowerManager.WakeLock wakeLock;
        try {
            if (last_automata_state == state && !JoH.ratelimit("jam-g5-dupe-auto", 2)) {
                UserError.Log.d(this.TAG, "Ignoring duplicate automata state within 2 seconds: " + state);
            }
            switch (AnonymousClass1.$SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[state.ordinal()]) {
                case 1:
                    initialize();
                    break;
                case 2:
                    scan_for_device();
                    break;
                case 3:
                    connect_to_device();
                    break;
                case 4:
                    retry_backoff = 0L;
                    enable_features_and_listen();
                    break;
                case 5:
                    changeState(state.next());
                    break;
                case 6:
                    check_calibrate();
                    break;
                case 7:
                    get_data();
                    break;
                case 8:
                    if (JoH.pratelimit("medtrum-set-time-" + serial, 60)) {
                        sendTx(new TimeTx());
                        break;
                    } else {
                        changeState(state.next());
                        break;
                    }
                case 9:
                    sendTx(new ConnParamTx());
                    break;
                case 10:
                    stopConnect();
                    setRetryTimer();
                    state = STATE.CLOSED;
                    break;
                case 11:
                    setRetryTimer();
                    break;
                case 12:
                    status("Listening");
                    if (notificationSubscription == null || notificationSubscription.isUnsubscribed()) {
                        changeState(STATE.SCAN);
                        break;
                    }
                    break;
            }
        } finally {
            JoH.releaseWakeLock(wakeLock);
        }
        last_automata_state = state;
        wakeLock = JoH.getWakeLock("jam-g5-automata", UsbId.SILABS_CP2102);
        return true;
    }

    private void initialize() {
        long serial2 = Medtrum.getSerial();
        serial = serial2;
        if (serial2 != 0) {
            changeState(STATE.SCAN);
            return;
        }
        if (JoH.ratelimit("medtrum-cannot-start", R$styleable.AppCompatTheme_windowFixedWidthMajor)) {
            UserError.Log.e(this.TAG, "Medtrum cannot start without serial number - please rescan");
        }
        JoH.static_toast_long("Medtrum cannot start without serial number - please rescan");
    }

    private void scan_for_device() {
        status(xdrip.gs(2131756235));
        UserError.Log.d(this.TAG, "Scanning for device");
        scanner.setAddress(address).scan();
    }

    private void connect_to_device() {
        if (JoH.quietratelimit("medtrum-connect-cooldown", 2)) {
            status("Connecting");
            UserError.Log.d(this.TAG, "Connecting to device: " + address);
            connect_to_device(false);
        }
    }

    private synchronized void enable_features_and_listen() {
        UserError.Log.d(this.TAG, "enable features - enter");
        stopListening();
        if (this.connection != null) {
            Observable observable = this.connection.setupNotification(Const.CGM_CHARACTERISTIC_NOTIFY);
            TimeUnit timeUnit = TimeUnit.SECONDS;
            notificationSubscription = new Subscription(observable.timeout(7200L, timeUnit).observeOn(Schedulers.newThread()).doOnNext(new Consumer() {
                public final void accept(Object obj) {
                    MedtrumCollectionService.this.lambda$enable_features_and_listen$0((Observable) obj);
                }
            }).flatMap(new Function() {
                public final Object apply(Object obj) {
                    ObservableSource lambda$enable_features_and_listen$1;
                    lambda$enable_features_and_listen$1 = MedtrumCollectionService.lambda$enable_features_and_listen$1((Observable) obj);
                    return lambda$enable_features_and_listen$1;
                }
            }).subscribe(new Consumer() {
                public final void accept(Object obj) {
                    MedtrumCollectionService.this.lambda$enable_features_and_listen$2((byte[]) obj);
                }
            }, new Consumer() {
                public final void accept(Object obj) {
                    MedtrumCollectionService.this.lambda$enable_features_and_listen$3((Throwable) obj);
                }
            }));
            final InboundStream inboundStream = new InboundStream();
            indicationSubscription = new Subscription(this.connection.setupIndication(Const.CGM_CHARACTERISTIC_INDICATE).timeout(7200L, timeUnit).observeOn(Schedulers.newThread()).doOnNext(new Consumer() {
                public final void accept(Object obj) {
                    MedtrumCollectionService.this.lambda$enable_features_and_listen$4((Observable) obj);
                }
            }).flatMap(new Function() {
                public final Object apply(Object obj) {
                    ObservableSource lambda$enable_features_and_listen$5;
                    lambda$enable_features_and_listen$5 = MedtrumCollectionService.lambda$enable_features_and_listen$5((Observable) obj);
                    return lambda$enable_features_and_listen$5;
                }
            }).subscribe(new Consumer() {
                public final void accept(Object obj) {
                    MedtrumCollectionService.this.lambda$enable_features_and_listen$7(inboundStream, (byte[]) obj);
                }
            }, new Consumer() {
                public final void accept(Object obj) {
                    MedtrumCollectionService.this.lambda$enable_features_and_listen$8((Throwable) obj);
                }
            }));
        } else {
            UserError.Log.e(this.TAG, "Connection null when trying to set notifications");
        }
    }

    public void lambda$enable_features_and_listen$0(Observable observable) throws Exception {
        UserError.Log.d(this.TAG, "Notifications enabled");
    }

    public void lambda$enable_features_and_listen$2(byte[] bArr) throws Exception {
        PowerManager.WakeLock wakeLock = JoH.getWakeLock("medtrum-receive-n", UsbId.SILABS_CP2102);
        try {
            UserError.Log.d(this.TAG, "Received notification bytes: " + JoH.bytesToHex(bArr));
            lastInteractionTime = JoH.tsl();
            setFailOverTimer();
            lastAnnex = new AnnexARx(bArr);
            UserError.Log.d(this.TAG, "Notification: " + lastAnnex.toS());
            createRecordFromAnnexData(lastAnnex);
            backFillIfNeeded(lastAnnex);
        } finally {
            JoH.releaseWakeLock(wakeLock);
        }
    }

    public void lambda$enable_features_and_listen$3(Throwable th) throws Exception {
        UserError.Log.d(this.TAG, "notification throwable: " + th);
    }

    public void lambda$enable_features_and_listen$4(Observable observable) throws Exception {
        UserError.Log.d(this.TAG, "Indications enabled");
        sendTx(new AuthTx(serial));
    }

    public void lambda$enable_features_and_listen$7(final InboundStream inboundStream, byte[] bArr) throws Exception {
        PowerManager.WakeLock wakeLock = JoH.getWakeLock("medtrum-receive-i", UsbId.SILABS_CP2102);
        try {
            UserError.Log.d(this.TAG, "Received indication bytes: " + JoH.bytesToHex(bArr));
            if (inboundStream.hasSomeData() && JoH.msSince(lastInteractionTime) > 10000) {
                UserError.Log.d(this.TAG, "Resetting stream due to earlier timeout");
            }
            lastInteractionTime = JoH.tsl();
            inboundStream.push(bArr);
            if (!checkAndProcessInboundStream(inboundStream)) {
                Inevitable.task("mt-reset-stream-no-data", 3000L, new Runnable() {
                    @Override
                    public final void run() {
                        MedtrumCollectionService.this.lambda$enable_features_and_listen$6(inboundStream);
                    }
                });
            }
        } finally {
            JoH.releaseWakeLock(wakeLock);
        }
    }

    public void lambda$enable_features_and_listen$6(InboundStream inboundStream) {
        if (inboundStream.hasSomeData()) {
            UserError.Log.d(this.TAG, "Resetting stream as incomplete after 3s");
            inboundStream.reset();
        }
    }

    public void lambda$enable_features_and_listen$8(Throwable th) throws Exception {
        UserError.Log.d(this.TAG, "indication throwable: " + th);
    }

    private boolean checkAndProcessInboundStream(InboundStream inboundStream) {
        if (!inboundStream.isComplete()) {
            return false;
        }
        final byte[] byteSequence = inboundStream.getByteSequence();
        inboundStream.reset();
        Inevitable.task("mt-stream-done:" + JoH.tsl(), 50L, new Runnable() {
            @Override
            public final void run() {
                MedtrumCollectionService.this.lambda$checkAndProcessInboundStream$9(byteSequence);
            }
        });
        return true;
    }

    private synchronized void connect_to_device(boolean z) {
        if (state == STATE.CONNECT) {
            if (address != null && address.length() > 6) {
                status("Connecting");
                stopConnect();
                this.bleDevice = this.rxBleClient.getBleDevice(address);
                stateSubscription = new Subscription(this.bleDevice.observeConnectionStateChanges().subscribeOn(Schedulers.io()).subscribe(new Consumer() {
                    public final void accept(Object obj) {
                        MedtrumCollectionService.this.onConnectionStateChange((RxBleConnection.RxBleConnectionState) obj);
                    }
                }, new Consumer() {
                    public final void accept(Object obj) {
                        MedtrumCollectionService.this.lambda$connect_to_device$10((Throwable) obj);
                    }
                }));
                listen_connected = false;
                connectionSubscription = new Subscription(this.bleDevice.establishConnection(false).timeout(7200L, TimeUnit.SECONDS).subscribeOn(Schedulers.io()).subscribe(new Consumer() {
                    public final void accept(Object obj) {
                        MedtrumCollectionService.this.onConnectionReceived((RxBleConnection) obj);
                    }
                }, new Consumer() {
                    public final void accept(Object obj) {
                        MedtrumCollectionService.this.onConnectionFailure((Throwable) obj);
                    }
                }));
            } else {
                UserError.Log.wtf(this.TAG, "No transmitter mac address!");
                changeState(STATE.SCAN);
            }
        } else {
            UserError.Log.wtf(this.TAG, "Attempt to connect when not in CONNECT state");
        }
    }

    public void lambda$connect_to_device$10(Throwable th) throws Exception {
        UserError.Log.wtf(this.TAG, "Got Error from state subscription: " + th);
    }

    private void get_data() {
        status("Asking for data");
        sendTx(new StatusTx());
    }

    private void check_calibrate() {
        Pair<Long, Integer> calibration = Medtrum.getCalibration();
        if (calibration != null) {
            status("Calibrating");
            try {
                sendTx(new CalibrateTx(serial, ((Long) calibration.first).longValue(), ((Integer) calibration.second).intValue()));
                return;
            } catch (InvalidAlgorithmParameterException e) {
                UserError.Log.wtf(this.TAG, "Cannot calibrate: " + e);
                return;
            }
        }
        changeState(state.next());
    }

    private void sendTx(final BaseMessage baseMessage) {
        if (this.connection != null) {
            try {
                this.connection.writeCharacteristic(Const.CGM_CHARACTERISTIC_INDICATE, JamBaseBluetoothService.nn(baseMessage.getByteSequence())).subscribe(new Consumer() {
                    public final void accept(Object obj) {
                        MedtrumCollectionService.this.lambda$sendTx$11(baseMessage, (byte[]) obj);
                    }
                }, new Consumer() {
                    public final void accept(Object obj) {
                        MedtrumCollectionService.this.lambda$sendTx$12(baseMessage, (Throwable) obj);
                    }
                });
            } catch (NullPointerException e) {
                UserError.Log.e(this.TAG, "Race condition when writing characteristic: " + e);
            }
        }
    }

    public void lambda$sendTx$11(BaseMessage baseMessage, byte[] bArr) throws Exception {
        UserError.Log.d(this.TAG, "Wrote " + baseMessage.getClass().getSimpleName() + " request: ");
    }

    public void lambda$sendTx$12(BaseMessage baseMessage, Throwable th) throws Exception {
        UserError.Log.e(this.TAG, "Failed to write " + baseMessage.getClass().getSimpleName() + " " + th);
    }

    public void lambda$checkAndProcessInboundStream$9(byte[] bArr) {
        String str;
        boolean z;
        if (bArr == null) {
            UserError.Log.e(this.TAG, "Was passed null in parseInbound");
        }
        if (bArr.length < 2) {
            UserError.Log.e(this.TAG, "Packet too short");
            return;
        }
        int i = bArr[1] & 255;
        if (i == 5) {
            AuthRx authRx = new AuthRx(bArr);
            if (authRx.isValid()) {
                status("Authenticated");
            } else {
                errorStatus("AUTHENTICATION FAILED!");
                if (JoH.ratelimit("medtrum-auth-fail", 600)) {
                    UserError.Log.wtf(this.TAG, "Auth packet failure: " + serial + authRx.toS());
                }
            }
            changeState(state.next());
            return;
        }
        if (i == 10) {
            UserError.Log.d(this.TAG, "Got connection parameter reply");
            ConnParamRx connParamRx = new ConnParamRx(bArr);
            status("Parameter reply");
            if (!connParamRx.isValid()) {
                UserError.Log.e(this.TAG, "Got invalid connection parameter reply msg");
            }
            changeState(state.next());
            return;
        }
        if (i == 87) {
            TimeRx timeRx = new TimeRx(bArr);
            StringBuilder sb = new StringBuilder();
            sb.append("Got time set reply: ");
            sb.append(timeRx.isValid() ? "VALID" : "INVALID");
            String sb2 = sb.toString();
            if (timeRx.isValid()) {
                status("Set time");
                UserError.Log.d(this.TAG, sb2);
            } else {
                status("Error setting time");
                UserError.Log.e(this.TAG, sb2);
            }
            changeState(STATE.SET_CONN_PARAM);
            return;
        }
        switch (i) {
            case 64:
                UserError.Log.d(this.TAG, "Got calibration reply");
                Medtrum.clearCalibration();
                CalibrateRx calibrateRx = new CalibrateRx(bArr);
                if (calibrateRx.isOk()) {
                    str = "Calibration OK";
                } else {
                    str = "Calibration Error " + calibrateRx.getErrorCode();
                }
                status(str);
                UserError.Log.ueh(this.TAG, str);
                JoH.static_toast_long(str);
                changeState(state.next());
                break;
            case androidx.constraintlayout.widget.R$styleable.ConstraintSet_layout_constraintVertical_bias:
                StatusRx statusRx = new StatusRx(bArr);
                UserError.Log.d(this.TAG, statusRx.toS());
                if (statusRx.isValid()) {
                    lastAnnex = statusRx.getAnnex();
                    statusRx.getAnnex().processForTimeKeeper(serial);
                    createRecordFromAnnexData(statusRx.getAnnex());
                    z = backFillIfNeeded(statusRx.getAnnex());
                } else {
                    z = false;
                }
                changeState(state.next(), z ? 1500 : 100);
                break;
            case 66:
                UserError.Log.d(this.TAG, "Got backfill reply");
                status("Got back fill");
                processBackFillPacket(bArr);
                break;
            default:
                UserError.Log.d(this.TAG, "Unknown inbound opcode: " + i);
                UserError.Log.e(this.TAG, "Received unknown inbound packet: " + HexDump.dumpHexString(bArr));
                break;
        }
    }

    private void processBackFillPacket(byte[] bArr) {
        BackFillRx backFillRx;
        String str;
        BackFillRx backFillRx2 = new BackFillRx(bArr);
        UserError.Log.d(this.TAG, backFillRx2.toS());
        if (backFillRx2.isOk()) {
            List<Integer> rawList = backFillRx2.getRawList();
            if (rawList != null) {
                int i = 0;
                boolean z = false;
                while (i < rawList.size()) {
                    long timeStampFromTickCounter = TimeKeeper.timeStampFromTickCounter(serial, backFillRx2.sequenceStart + i);
                    UserError.Log.d(this.TAG, "Backsie:  id:" + (backFillRx2.sequenceStart + i) + " raw:" + rawList.get(i) + " @ " + JoH.dateTimeText(timeStampFromTickCounter));
                    long msSince = JoH.msSince(timeStampFromTickCounter);
                    if (msSince > 21600000 || msSince < 0) {
                        backFillRx = backFillRx2;
                        UserError.Log.wtf(this.TAG, "Backfill timestamp unrealistic: " + JoH.dateTimeText(timeStampFromTickCounter) + " (ignored)");
                    } else {
                        double glucose = backFillRx2.getGlucose(rawList.get(i).intValue());
                        int sensorRawEmulateDex = backFillRx2.getSensorRawEmulateDex(rawList.get(i).intValue());
                        if (BgReading.getForPreciseTimestamp(timeStampFromTickCounter, 150000L) == null) {
                            if (isNative()) {
                                if (glucose > BgReading.BESTOFFSET) {
                                    backFillRx = backFillRx2;
                                    str = "Medtrum2nd";
                                    BgReading.bgReadingInsertMedtrum(glucose, timeStampFromTickCounter, "Backfill", sensorRawEmulateDex);
                                    UserError.Log.d(this.TAG, "Adding native backfilled reading: " + JoH.dateTimeText(timeStampFromTickCounter) + " " + BgGraphBuilder.unitized_string_static(glucose));
                                } else {
                                    backFillRx = backFillRx2;
                                    str = "Medtrum2nd";
                                }
                                double d = sensorRawEmulateDex;
                                BgReading createFromRawNoSave = BgReading.createFromRawNoSave(null, null, d, d, timeStampFromTickCounter);
                                double d2 = createFromRawNoSave.calculated_value;
                                if (d2 > BgReading.BESTOFFSET) {
                                    Prediction.create(createFromRawNoSave.timestamp, (int) d2, str).save();
                                }
                            } else {
                                backFillRx = backFillRx2;
                                if (glucose > BgReading.BESTOFFSET) {
                                    Prediction.create(timeStampFromTickCounter, (int) glucose, "Medtrum2nd").save();
                                }
                                double d3 = sensorRawEmulateDex;
                                BgReading create = BgReading.create(d3, d3, xdrip.getAppContext(), Long.valueOf(timeStampFromTickCounter));
                                if (create != null) {
                                    UserError.Log.d(this.TAG, "Backfilled BgReading created: " + create.uuid + " " + JoH.dateTimeText(create.timestamp));
                                } else {
                                    UserError.Log.d(this.TAG, "BgReading null!");
                                }
                            }
                            Inevitable.task("backfill-ui-update", 3000L, new Runnable() {
                                @Override
                                public final void run() {
                                    Home.staticRefreshBGChartsOnIdle();
                                }
                            });
                            z = true;
                        } else {
                            backFillRx = backFillRx2;
                        }
                    }
                    i++;
                    backFillRx2 = backFillRx;
                }
                if (z || rawList.size() >= requestedBackfillSize || !JoH.ratelimit("mt-backfill-repeat", 60)) {
                    return;
                }
                UserError.Log.d(this.TAG, "Requesting additional backfill with offset: " + rawList.size());
                backFillIfNeeded(lastAnnex, rawList.size());
                return;
            }
            return;
        }
        UserError.Log.e(this.TAG, "Backfill data reports not ok");
    }

    private boolean backFillIfNeeded(AnnexARx annexARx) {
        return backFillIfNeeded(annexARx, 0);
    }

    private boolean backFillIfNeeded(AnnexARx annexARx, int i) {
        Pair<Long, Long> check;
        if (annexARx != null && annexARx.isStateOkForBackFill() && (check = BackfillAssessor.check()) != null) {
            int tickCounterFromTimeStamp = TimeKeeper.tickCounterFromTimeStamp(serial, ((Long) check.first).longValue());
            int tickCounterFromTimeStamp2 = TimeKeeper.tickCounterFromTimeStamp(serial, ((Long) check.second).longValue());
            int i2 = annexARx.sensorAge;
            if (tickCounterFromTimeStamp2 >= i2) {
                tickCounterFromTimeStamp2 = i2 - 1;
            }
            if (tickCounterFromTimeStamp < 62) {
                tickCounterFromTimeStamp = 62;
            }
            if (tickCounterFromTimeStamp2 < 1) {
                tickCounterFromTimeStamp2 = 1;
            }
            int i3 = tickCounterFromTimeStamp + i;
            if (i3 != tickCounterFromTimeStamp2 && tickCounterFromTimeStamp2 > i3) {
                if (tickCounterFromTimeStamp2 - i3 > 30) {
                    tickCounterFromTimeStamp2 = i3 + 30;
                }
                UserError.Log.d(this.TAG, "Ask backfill: start: " + i3 + "  end: " + tickCounterFromTimeStamp2);
                requestedBackfillSize = tickCounterFromTimeStamp2 - i3;
                sendTx(new BackFillTx(i3, tickCounterFromTimeStamp2));
                return true;
            }
            UserError.Log.d(this.TAG, "Not backfilling with start and end tick at: " + i3 + " " + tickCounterFromTimeStamp2);
        }
        return false;
    }

    private boolean isNative() {
        return Pref.getBooleanDefaultFalse("medtrum_use_native");
    }

    private void createRecordFromAnnexData(AnnexARx annexARx) {
        if (annexARx == null) {
            return;
        }
        status("Got data");
        if (lastRecordTime == 0) {
            lastRecordTime = PersistentStore.getLong("mtc-last-record-time");
        }
        if (JoH.msSince(lastRecordTime) > 280000) {
            UserError.Log.d(this.TAG, "Creating transmitter data from record annex");
            TransmitterData create = TransmitterData.create(annexARx.getSensorRawEmulateDex(), annexARx.getSensorRawEmulateDex(), annexARx.getBatteryPercent(), JoH.tsl());
            if (create != null) {
                long j = create.timestamp;
                lastRecordTime = j;
                PersistentStore.setLong("mtc-last-record-time", j);
                if (create.raw_data > BgReading.BESTOFFSET) {
                    if (annexARx.getState() == SensorState.Ok || annexARx.getState() == SensorState.NotCalibrated) {
                        double calculatedGlucose = annexARx.calculatedGlucose();
                        if (isNative()) {
                            if (calculatedGlucose > BgReading.BESTOFFSET) {
                                BgReading.bgReadingInsertMedtrum(calculatedGlucose, JoH.tsl(), null, create.raw_data);
                            } else if (annexARx.getState() == SensorState.NotCalibrated) {
                                UserError.Log.d(this.TAG, "Just adding raw data");
                                BgReading.create(create.raw_data, create.filtered_data, xdrip.getAppContext(), Long.valueOf(create.timestamp));
                            }
                            double d = create.raw_data;
                            BgReading createFromRawNoSave = BgReading.createFromRawNoSave(null, null, d, d, create.timestamp);
                            double d2 = createFromRawNoSave.calculated_value;
                            if (d2 > BgReading.BESTOFFSET) {
                                Prediction.create(createFromRawNoSave.timestamp, (int) d2, "Medtrum2nd").save();
                                UserError.Log.d(this.TAG, "Created secondary trace for value: " + createFromRawNoSave.calculated_value);
                                return;
                            }
                            return;
                        }
                        if (calculatedGlucose > BgReading.BESTOFFSET) {
                            Prediction.create(JoH.tsl(), (int) calculatedGlucose, "Medtrum2nd").save();
                            UserError.Log.d(this.TAG, "Saving extra data");
                        }
                        BgReading create2 = BgReading.create(create.raw_data, create.filtered_data, xdrip.getAppContext(), Long.valueOf(create.timestamp));
                        if (create2 != null) {
                            UserError.Log.d(this.TAG, "BgReading created: " + create2.uuid + " " + JoH.dateTimeText(create2.timestamp));
                            return;
                        }
                        UserError.Log.d(this.TAG, "BgReading null!");
                        return;
                    }
                    UserError.Log.d(this.TAG, "Ignoring due to sensor state: " + annexARx.getState());
                    return;
                }
                UserError.Log.d(this.TAG, "Raw data was invalid so not proceeding");
                return;
            }
            return;
        }
        UserError.Log.d(this.TAG, "Not creating data record so close to previous");
    }

    static class AnonymousClass1 {
        static final int[] $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE;
        static final int[] $SwitchMap$com$polidea$rxandroidble2$RxBleConnection$RxBleConnectionState;

        static {
            int[] iArr = new int[RxBleConnection.RxBleConnectionState.values().length];
            $SwitchMap$com$polidea$rxandroidble2$RxBleConnection$RxBleConnectionState = iArr;
            try {
                iArr[RxBleConnection.RxBleConnectionState.CONNECTING.ordinal()] = 1;
            } catch (NoSuchFieldError unused) {
            }
            try {
                $SwitchMap$com$polidea$rxandroidble2$RxBleConnection$RxBleConnectionState[RxBleConnection.RxBleConnectionState.CONNECTED.ordinal()] = 2;
            } catch (NoSuchFieldError unused2) {
            }
            try {
                $SwitchMap$com$polidea$rxandroidble2$RxBleConnection$RxBleConnectionState[RxBleConnection.RxBleConnectionState.DISCONNECTING.ordinal()] = 3;
            } catch (NoSuchFieldError unused3) {
            }
            try {
                $SwitchMap$com$polidea$rxandroidble2$RxBleConnection$RxBleConnectionState[RxBleConnection.RxBleConnectionState.DISCONNECTED.ordinal()] = 4;
            } catch (NoSuchFieldError unused4) {
            }
            int[] iArr2 = new int[STATE.values().length];
            $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE = iArr2;
            try {
                iArr2[STATE.INIT.ordinal()] = 1;
            } catch (NoSuchFieldError unused5) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.SCAN.ordinal()] = 2;
            } catch (NoSuchFieldError unused6) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.CONNECT.ordinal()] = 3;
            } catch (NoSuchFieldError unused7) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.ENABLE.ordinal()] = 4;
            } catch (NoSuchFieldError unused8) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.DISCOVER.ordinal()] = 5;
            } catch (NoSuchFieldError unused9) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.CALIBRATE.ordinal()] = 6;
            } catch (NoSuchFieldError unused10) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.GET_DATA.ordinal()] = 7;
            } catch (NoSuchFieldError unused11) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.SET_TIME.ordinal()] = 8;
            } catch (NoSuchFieldError unused12) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.SET_CONN_PARAM.ordinal()] = 9;
            } catch (NoSuchFieldError unused13) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.CLOSE.ordinal()] = 10;
            } catch (NoSuchFieldError unused14) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.CLOSED.ordinal()] = 11;
            } catch (NoSuchFieldError unused15) {
            }
            try {
                $SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[STATE.LISTEN.ordinal()] = 12;
            } catch (NoSuchFieldError unused16) {
            }
        }
    }

    public synchronized void onConnectionStateChange(RxBleConnection.RxBleConnectionState rxBleConnectionState) {
        String str = "Unknown";
        int i = AnonymousClass1.$SwitchMap$com$polidea$rxandroidble2$RxBleConnection$RxBleConnectionState[rxBleConnectionState.ordinal()];
        if (i == 1) {
            str = "Connecting";
        } else if (i == 2) {
            str = "Connected";
            retry_backoff = 0L;
        } else if (i == 3) {
            str = "Disconnecting";
        } else if (i == 4) {
            str = "Disconnected";
            status("Disconnected");
            changeState(STATE.CLOSE);
        }
        status(str);
        if (str.equals("Disconnecting")) {
            tryGattRefresh(this.connection);
        }
    }

    public void onConnectionReceived(RxBleConnection rxBleConnection) {
        listen_connected = true;
        status("Connected");
        this.connection = rxBleConnection;
        if (rxBleConnection != null) {
            changeState(STATE.ENABLE);
        } else {
            UserError.Log.d(this.TAG, "New connection null!");
            changeState(STATE.CLOSE);
        }
    }

    public void onConnectionFailure(Throwable th) {
        if (listen_connected) {
            status("Disconnected");
        } else {
            status("Connection failure");
        }
        UserError.Log.d(this.TAG, "Connection Disconnected/Failed: " + th);
        stopConnect();
        changeState(STATE.CLOSE);
        setRetryTimer();
    }

    private synchronized void stopConnect() {
        UserError.Log.d(this.TAG, "Stopping connection with: " + address);
        stopListening();
        if (connectionSubscription != null) {
            connectionSubscription.unsubscribe();
        }
        if (stateSubscription != null) {
            stateSubscription.unsubscribe();
        }
    }

    private void stopListening() {
        if (indicationSubscription != null) {
            indicationSubscription.unsubscribe();
        }
        if (notificationSubscription != null) {
            notificationSubscription.unsubscribe();
        }
    }

    public void changeState(STATE state2) {
        changeState(state2, 100);
    }

    public void changeState(STATE state2, int i) {
        if ((state == STATE.CLOSED || state == STATE.CLOSE) && state2 == STATE.CLOSE) {
            UserError.Log.d(this.TAG, "Not closing as already closed");
            return;
        }
        if (state2 != state) {
            UserError.Log.d(this.TAG, "Changing state from: " + state + " to " + state2);
            state = state2;
            background_automata(i);
            return;
        }
        UserError.Log.d(this.TAG, "Not changing state as already in state: " + state2);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (scanner == null) {
            scanner = new Scanner();
        }
        scanner.setTag(this.TAG);
        scanner.addCallBack(this, this.TAG);
        DisconnectReceiver.addCallBack(this, this.TAG);
        UserError.Log.d(this.TAG, "SERVICE CREATED - SERVICE CREATED");
        JamBaseBluetoothService.enableBuggySamsungIfNeeded(this.TAG);
    }

    @Override
    public void onDestroy() {
        try {
            scanner.stop();
        } catch (Exception unused) {
        }
        stopConnect();
        DisconnectReceiver.removeCallBack(this.TAG);
        wakeup_time = 0L;
        last_automata_state = STATE.CLOSED;
        status("Stopped");
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int i, int i2) {
        xdrip.checkAppContext(getApplicationContext());
        if (shouldServiceRun()) {
            PowerManager.WakeLock wakeLock = JoH.getWakeLock("medtrum-start-service", 600000);
            try {
                UserError.Log.d(this.TAG, "WAKE UP WAKE UP WAKE UP WAKE UP @ " + JoH.dateTimeText(JoH.tsl()) + " State: " + state);
                setFailOverTimer();
                long j = wakeup_time;
                if (j > 0) {
                    long msSince = JoH.msSince(j);
                    wakeup_jitter = msSince;
                    if (msSince < 0) {
                        UserError.Log.d(this.TAG, "Woke up Early..");
                    } else if (msSince > 1000) {
                        UserError.Log.d(this.TAG, "Wake up, time jitter: " + JoH.niceTimeScalar(wakeup_jitter));
                        if (wakeup_jitter > 10000 && !JoH.buggy_samsung && JoH.isSamsung()) {
                            UserError.Log.wtf(this.TAG, "Enabled wake workaround due to jitter of: " + JoH.niceTimeScalar(wakeup_jitter));
                            JoH.buggy_samsung = true;
                            PersistentStore.incrementLong("buggy-samsung-enabled");
                            max_wakeup_jitter = 0L;
                        } else {
                            max_wakeup_jitter = Math.max(max_wakeup_jitter, wakeup_jitter);
                        }
                    }
                    wakeup_time = 0L;
                }
                retry_time = 0L;
                last_wake_up_time = JoH.tsl();
                try {
                    address = ActiveBluetoothDevice.first().address;
                } catch (NullPointerException unused) {
                }
                processInitialState();
                background_automata();
                return 1;
            } finally {
                JoH.releaseWakeLock(wakeLock);
            }
        }
        UserError.Log.d(this.TAG, "Should not be running so shutting down");
        stopSelf();
        return 2;
    }

    private void processInitialState() {
        int i = AnonymousClass1.$SwitchMap$com$eveningoutpost$dexdrip$cgm$medtrum$MedtrumCollectionService$STATE[state.ordinal()];
        if (i == 1 || i == 2) {
            return;
        }
        if (i == 10 || i == 11) {
            UserError.Log.d(this.TAG, "Changing from initial state of " + state + " to INIT");
            state = STATE.INIT;
            return;
        }
        if (JoH.msSince(lastInteractionTime) > 300000) {
            UserError.Log.d(this.TAG, "Changing from initial state of " + state + " to INIT due to interaction timeout");
            state = STATE.INIT;
        }
    }

    @Override
    public void btCallback(String str, String str2) {
        UserError.Log.d(this.TAG, "Processing callback: " + str + " :: " + str2);
        if (str.equals(address)) {
            str2.hashCode();
            switch (str2) {
                case "SCAN_FAILED":
                    status("Scan Failed!");
                    changeState(STATE.CONNECT);
                    break;
                case "SCAN_TIMEOUT":
                    status("Scan timed out");
                    changeState(STATE.CONNECT);
                    break;
                case "SCAN_FOUND":
                    changeState(STATE.CONNECT);
                    break;
                case "DISCONNECTED":
                    changeState(STATE.CLOSE);
                    break;
                default:
                    UserError.Log.e(this.TAG, "Unknown status callback for: " + str + " with " + str2);
                    break;
            }
        }
        UserError.Log.d(this.TAG, "Ignoring: " + str2 + " for " + str + " as we are using: " + address);
    }

    private static void status(String str) {
        lastState = str + " " + JoH.hourMinuteString();
        UserError.Log.d(STATIC_TAG, "Status: " + lastState);
    }

    private static void errorStatus(String str) {
        lastErrorState = str + " " + JoH.hourMinuteString();
        UserError.Log.e(STATIC_TAG, lastErrorState);
    }

    @SuppressLint({"ObsoleteSdkInt"})
    private static boolean shouldServiceRun() {
        return DexCollectionType.getDexCollectionType().equals(DexCollectionType.Medtrum);
    }

    private void setRetryTimer() {
        Inevitable.task("mt-set-retry", 500L, new Runnable() {
            @Override
            public final void run() {
                MedtrumCollectionService.this.setRetryTimerReal();
            }
        });
    }

    public void setRetryTimerReal() {
        if (shouldServiceRun()) {
            long whenToRetryNext = whenToRetryNext();
            UserError.Log.d(this.TAG, "setRetryTimer: Restarting in: " + (whenToRetryNext / 1000) + " seconds");
            PendingIntent pendingIntent = WakeLockTrampoline.getPendingIntent(getClass(), 1015);
            serviceIntent = pendingIntent;
            retry_time = JoH.wakeUpIntent(this, whenToRetryNext, pendingIntent);
            wakeup_time = JoH.tsl() + whenToRetryNext;
            return;
        }
        UserError.Log.d(this.TAG, "Not setting retry timer as service should not be running");
    }

    private void setFailOverTimer() {
        if (shouldServiceRun()) {
            if (JoH.quietratelimit("mt-failover-cooldown", 30)) {
                UserError.Log.d(this.TAG, "setFailOverTimer: Restarting in: 420 seconds");
                PendingIntent pendingIntent = WakeLockTrampoline.getPendingIntent(getClass(), 1016);
                serviceFailoverIntent = pendingIntent;
                failover_time = JoH.wakeUpIntent(this, 420000L, pendingIntent);
                return;
            }
            return;
        }
        UserError.Log.d(this.TAG, "Not setting retry timer as service should not be running");
    }

    private static long whenToRetryNext() {
        long j = retry_backoff + 1000;
        retry_backoff = j;
        if (j > 60000) {
            retry_backoff = 60000L;
        }
        return retry_backoff + 10000;
    }

    public static SpannableString nanoStatus() {
        if (JoH.emptyString(lastErrorState)) {
            return null;
        }
        return Span.colorSpan(lastErrorState, StatusItem.Highlight.CRITICAL.color());
    }

    public static List<StatusItem> megaStatus() {
        if (lastAnnex == null) {
            lastAnnex = Medtrum.getLastAdvertAnnex();
        }
        ArrayList arrayList = new ArrayList();
        arrayList.add(new StatusItem("Phone Service State", lastState));
        if (!JoH.emptyString(lastErrorState)) {
            arrayList.add(new StatusItem("Error", lastErrorState, StatusItem.Highlight.BAD));
        }
        if (lastAnnex != null) {
            arrayList.add(new StatusItem("Battery", lastAnnex.getBatteryPercent() + "%"));
            AnnexARx annexARx = lastAnnex;
            if (annexARx.charging) {
                arrayList.add(new StatusItem("Charging", annexARx.charged ? "Charged" : "On charge"));
            }
            String description = lastAnnex.getState().getDescription();
            SensorState state2 = lastAnnex.getState();
            SensorState sensorState = SensorState.Ok;
            arrayList.add(new StatusItem("Sensor State", description, state2 == sensorState ? StatusItem.Highlight.GOOD : StatusItem.Highlight.NORMAL));
            if (lastAnnex.getState() == SensorState.WarmingUp1) {
                arrayList.add(new StatusItem("Warm up", "Initial warm up", StatusItem.Highlight.NOTICE));
            } else if (lastAnnex.getState() == SensorState.WarmingUp2) {
                long sensorAgeInMs = (7200000 - lastAnnex.getSensorAgeInMs()) / 60000;
                if (sensorAgeInMs > -1 && sensorAgeInMs < 121) {
                    arrayList.add(new StatusItem("Warmup left", sensorAgeInMs + " mins", StatusItem.Highlight.NOTICE));
                }
            }
            if (lastAnnex.getSensorAgeInMs() > 0) {
                arrayList.add(new StatusItem("Sensor Age", lastAnnex.getNiceSensorAge()));
            }
            if (lastAnnex.sensorGood) {
                arrayList.add(new StatusItem("Sensor", "Good", StatusItem.Highlight.GOOD));
            }
            if (lastAnnex.sensorError) {
                arrayList.add(new StatusItem("Sensor Error", "Error", StatusItem.Highlight.BAD));
            }
            if (lastAnnex.sensorFail) {
                arrayList.add(new StatusItem("Sensor Fail", "FAILED", StatusItem.Highlight.CRITICAL));
            }
            if (lastAnnex.calibrationErrorA) {
                arrayList.add(new StatusItem("Calibration Error", "Error A", StatusItem.Highlight.BAD));
            }
            if (lastAnnex.calibrationErrorB) {
                arrayList.add(new StatusItem("Calibration Error", "Error B", StatusItem.Highlight.BAD));
            }
            Pair<Long, Integer> calibration = Medtrum.getCalibration();
            if (calibration != null) {
                arrayList.add(new StatusItem("Blood test", BgGraphBuilder.unitized_string_with_units_static(((Integer) calibration.second).intValue()) + " @ " + JoH.hourMinuteString(((Long) calibration.first).longValue()), StatusItem.Highlight.NOTICE));
            }
            long j = serial;
            String str = "";
            if (j != 0) {
                int version = Medtrum.getVersion(j);
                arrayList.add(new StatusItem("Serial", "" + serial));
                if (version > 0) {
                    arrayList.add(new StatusItem("Firmware", "v" + JoH.qs(version / 100.0d, 2)));
                }
            }
            if (lastAnnex.getState() == sensorState || lastAnnex.getState() == SensorState.NotCalibrated) {
                arrayList.add(new StatusItem("Slope", JoH.qs(lastAnnex.calibrationSlope / 1000.0d, 3) + " (" + JoH.qs(1.0d / (lastAnnex.calibrationSlope / 1000.0d), 2) + ")"));
                arrayList.add(new StatusItem("Intercept", Integer.valueOf(lastAnnex.calibrationIntercept)));
                arrayList.add(new StatusItem("Raw Data", Integer.valueOf(lastAnnex.sensorRaw)));
            }
            if (lastAnnex.getState() == sensorState) {
                StringBuilder sb = new StringBuilder();
                sb.append(BgGraphBuilder.unitized_string_with_units_static(lastAnnex.calculatedGlucose()));
                if (!lastAnnex.recent()) {
                    str = " @ " + JoH.niceTimeScalarShort(JoH.msSince(lastAnnex.created));
                }
                sb.append(str);
                arrayList.add(new StatusItem("Last Glucose", sb.toString(), lastAnnex.recent() ? StatusItem.Highlight.NORMAL : StatusItem.Highlight.NOTICE));
            }
            long j2 = retry_time;
            if (j2 != 0) {
                arrayList.add(new StatusItem("Wake up in", JoH.niceTimeScalar(JoH.msTill(j2))));
            }
            long j3 = failover_time;
            if (j3 != 0) {
                arrayList.add(new StatusItem("System check in", JoH.niceTimeScalar(JoH.msTill(j3))));
            }
            if (Home.get_engineering_mode()) {
                arrayList.add(new StatusItem("Brain State", state.getString()));
                arrayList.add(new StatusItem("Last Interaction", JoH.niceTimeScalar(JoH.msSince(lastInteractionTime))));
                arrayList.add(new StatusItem("Last Wake Up", JoH.niceTimeScalar(JoH.msSince(last_wake_up_time))));
            }
        } else {
            arrayList.add(new StatusItem("Status Information", "Nothing Yet", StatusItem.Highlight.NOTICE));
        }
        return arrayList;
    }

    public static boolean isRunning() {
        return (lastState.equals("Not Running") || lastState.startsWith("Stop")) ? false : true;
    }

    public static boolean isCollecting() {
        return JoH.msSince(lastInteractionTime) < 300000;
    }

    public static void calibratePing() {
        if (shouldServiceRun()) {
            state = STATE.CALIBRATE;
            JoH.startService(MedtrumCollectionService.class);
        }
    }
}