UNPKG

@nebulae/angular-ble

Version:

A Web Bluetooth (Bluetooth Low Energy) module for angular (v2+)

895 lines 94.2 kB
/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */
import * as tslib_1 from "tslib";
import { Injectable, EventEmitter } from '@angular/core';
import { map, mergeMap, concat, mapTo, filter, takeUntil, scan, tap, take, timeout, retryWhen, delay } from 'rxjs/operators';
import { Observable, forkJoin, Subject, defer, fromEvent, of, from } from 'rxjs';
import { GattServices } from './gatt-services';
import { BrowserWebBluetooth } from '../platform/browser';
import { CypherAesService } from '../cypher/cypher-aes.service';
import { ConsoleLoggerService } from '../logger.service';
import * as i0 from "@angular/core";
import * as i1 from "../platform/browser";
import * as i2 from "../cypher/cypher-aes.service";
import * as i3 from "../logger.service";
var BluetoothService = /** @class */ (function (_super) {
    tslib_1.__extends(BluetoothService, _super);
    function BluetoothService(_webBle, cypherAesService, _console) {
        var _this = _super.call(this) || this;
        _this._webBle = _webBle;
        _this.cypherAesService = cypherAesService;
        _this._console = _console;
        _this.serviceCharacteristicVsSubscriptionList = {};
        _this.notifierSubject = new Subject();
        _this.notifierStartedSubject = new Subject();
        _this.bluetoothAvailable = false;
        _this._device$ = new EventEmitter();
        if (_webBle._ble) {
            _this.bluetoothAvailable = true;
        }
        return _this;
    }
    /**
     * @return {?}
     */
    BluetoothService.prototype.isBluetoothAvailable = /**
     * @return {?}
     */
    function () {
        return this.bluetoothAvailable;
    };
    /**
     * get the current device, if the device return null is because the connection has lost
     * @returns the current connceted device
     */
    /**
     * get the current device, if the device return null is because the connection has lost
     * @return {?} the current connceted device
     */
    BluetoothService.prototype.getDevice$ = /**
     * get the current device, if the device return null is because the connection has lost
     * @return {?} the current connceted device
     */
    function () {
        return this._device$;
    };
    /**
     * @return {?}
     */
    BluetoothService.prototype.getNotifierStartedSubject$ = /**
     * @return {?}
     */
    function () {
        return this.notifierStartedSubject;
    };
    /**
     * start a stream by notifiers characteristics
     * @param service The service to which the characteristic belongs
     * @param characteristic The characteristic whose value you want to listen
     * @param options object that contains the
     * startByte:number (required), stopByte:number (required),
     * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
     * @returns A DataView than contains the characteristic value
     */
    /**
     * start a stream by notifiers characteristics
     * @param {?} service The service to which the characteristic belongs
     * @param {?} characteristic The characteristic whose value you want to listen
     * @param {?} options object that contains the
     * startByte:number (required), stopByte:number (required),
     * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
     * @return {?} A DataView than contains the characteristic value
     */
    BluetoothService.prototype.startNotifierListener$ = /**
     * start a stream by notifiers characteristics
     * @param {?} service The service to which the characteristic belongs
     * @param {?} characteristic The characteristic whose value you want to listen
     * @param {?} options object that contains the
     * startByte:number (required), stopByte:number (required),
     * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
     * @return {?} A DataView than contains the characteristic value
     */
    function (service, characteristic, options) {
        var _this = this;
        return defer(function () {
            of(service)
                .pipe(tap(function () { return _this._console.log('Inicia el notifier con la instancia: ', _this.serviceCharacteristicVsSubscriptionList); }), filter(function (_) {
                return Object.keys(_this.serviceCharacteristicVsSubscriptionList).indexOf(service + "-" + characteristic) === -1;
            }))
                .subscribe(function () {
                _this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic] =
                    _this.buildNotifierListener$(service, characteristic, options).subscribe(function (message) {
                        _this.notifierSubject.next(message);
                    });
            }, function (err) {
                _this._console.log('[BLE::Info] Error in notifier: ', err);
            }, function () { });
            return of("notifier as subscribed: service= " + service + ", characteristic= " + characteristic);
        });
    };
    /**
     * @param {?} service
     * @param {?} characteristic
     * @return {?}
     */
    BluetoothService.prototype.stopNotifierListener$ = /**
     * @param {?} service
     * @param {?} characteristic
     * @return {?}
     */
    function (service, characteristic) {
        var _this = this;
        return defer(function () {
            // this.serviceCharacteristicVsSubscriptionList[`${service}-${characteristic}`].unsubscribe();
            delete _this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic];
            return of("the notifier of the characteristic " + characteristic + " as been stopped");
        });
    };
    /**
     * @param {?} service
     * @param {?} characteristic
     * @param {?} options
     * @return {?}
     */
    BluetoothService.prototype.buildNotifierListener$ = /**
     * @param {?} service
     * @param {?} characteristic
     * @param {?} options
     * @return {?}
     */
    function (service, characteristic, options) {
        var _this = this;
        return this.getPrimaryService$(service).pipe(tap(function (serviceInstance) { return _this._console.log("toma exitosamente el servicio ================> " + serviceInstance); }), mergeMap(function (primaryService) {
            return _this.getCharacteristic$(primaryService, characteristic)
                .pipe(tap(function (char) { return _this._console.log("toma exitosamente la caracteristica ================> " + char); }));
        }), mergeMap(function (char) {
            return defer(function () {
                // enable the characteristic notifier
                return char.startNotifications();
            }).pipe(retryWhen(function (error) {
                return error.pipe(tap(function () { return _this._console.log('ERROR EN EL startNotifications ================> '); }), delay(1000), take(5));
            }), tap(function () {
                _this.notifierStartedSubject.next(true);
                _this._console.log("incia las notifiaciones de la caracteristica 2 ================> " + characteristic);
            }), mergeMap(function (_) {
                // start the lister from the even characteristicvaluechanged to get all changes on the specific
                // characteristic
                return fromEvent(char, 'characteristicvaluechanged').pipe(takeUntil(fromEvent(char, 'gattserverdisconnected')), map(function (event) {
                    // get a event from the characteristic and map that
                    return {
                        startByteMatches: false,
                        stopByteMatches: false,
                        lengthMatches: false,
                        messageLength: 0,
                        timestamp: Date.now(),
                        data: Array.from(new Uint8Array((/** @type {?} */ ((/** @type {?} */ (event.target))
                            .value)).buffer))
                    };
                }), scan(function (acc, value) {
                    acc.timestamp = acc.timestamp === 0 ? value.timestamp : acc.timestamp;
                    // if the current accumulator value is a valid message, then is restarted to get the next
                    // message
                    if ((acc.lengthMatches &&
                        acc.startByteMatches &&
                        acc.stopByteMatches)
                        || acc.timestamp + 1000 < Date.now()) {
                        acc = {
                            startByteMatches: false,
                            stopByteMatches: false,
                            lengthMatches: false,
                            messageLength: 0,
                            timestamp: 0,
                            data: []
                        };
                    }
                    // validate the start byte
                    if (!acc.startByteMatches &&
                        value.data[0] === options.startByte) {
                        // get the message length using the start and end position
                        acc.messageLength =
                            new DataView(new Uint8Array(value.data.slice(options.lengthPosition.start, options.lengthPosition.end)).buffer).getInt16(0, false) +
                                (options.lengthPosition.lengthPadding
                                    ? options.lengthPosition.lengthPadding
                                    : 0);
                        // valid that the initial byte was found
                        acc.startByteMatches = true;
                    }
                    if (!acc.stopByteMatches &&
                        value.data[value.data.length - 1] === options.stopByte) {
                        // valid that the end byte was found
                        acc.stopByteMatches = true;
                    }
                    if (acc.startByteMatches) {
                        // merge the new data bytes to the old bytes
                        acc.data = acc.data.concat(value.data);
                    }
                    acc.lengthMatches =
                        acc.startByteMatches &&
                            acc.stopByteMatches &&
                            acc.messageLength === acc.data.length;
                    return acc;
                }, {
                    startByteMatches: false,
                    stopByteMatches: false,
                    lengthMatches: false,
                    messageLength: 0,
                    timestamp: 0,
                    data: []
                }), 
                // only publish the complete and valid message
                filter(function (data) {
                    return data.lengthMatches &&
                        data.startByteMatches &&
                        data.stopByteMatches;
                }), 
                // remove all custom data and publish the message data
                map(function (result) { return result.data; }));
            }));
        }));
    };
    /**
     * Send a request to the device and wait a unique response
     * @param message Message to send
     * @param service The service to which the characteristic belongs
     * @param characteristic The characteristic whose value you want to send the message
     * @param responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
     *  {position: 13, byteToMatch: 0x45}]
     * @param cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     */
    /**
     * Send a request to the device and wait a unique response
     * @param {?} message Message to send
     * @param {?} service The service to which the characteristic belongs
     * @param {?} characteristic The characteristic whose value you want to send the message
     * @param {?} responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
     *  {position: 13, byteToMatch: 0x45}]
     * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     * @return {?}
     */
    BluetoothService.prototype.sendAndWaitResponse$ = /**
     * Send a request to the device and wait a unique response
     * @param {?} message Message to send
     * @param {?} service The service to which the characteristic belongs
     * @param {?} characteristic The characteristic whose value you want to send the message
     * @param {?} responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
     *  {position: 13, byteToMatch: 0x45}]
     * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     * @return {?}
     */
    function (message, service, characteristic, responseType, cypherMasterKey) {
        this._console.log('[BLE::Info] Send message to device: ', this.cypherAesService.bytesTohex(message));
        return forkJoin(this.subscribeToNotifierListener(responseType, cypherMasterKey).pipe(take(1)), this.sendToNotifier$(message, service, characteristic)).pipe(map(function (_a) {
            var _b = tslib_1.__read(_a, 2), messageResp = _b[0], _ = _b[1];
            return messageResp;
        }), timeout(3000));
    };
    /**
     * Subscribe to the notifiers filtering by byte checking
     * @param filterOptions must specific the position and the byte to match Sample:
     * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
     * @param cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     */
    /**
     * Subscribe to the notifiers filtering by byte checking
     * @param {?} filterOptions must specific the position and the byte to match Sample:
     * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
     * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     * @return {?}
     */
    BluetoothService.prototype.subscribeToNotifierListener = /**
     * Subscribe to the notifiers filtering by byte checking
     * @param {?} filterOptions must specific the position and the byte to match Sample:
     * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
     * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     * @return {?}
     */
    function (filterOptions, cypherMasterKey) {
        var _this = this;
        return this.notifierSubject.pipe(map(function (messageUnformated) {
            /** @type {?} */
            var messageFormmated = /** @type {?} */ (messageUnformated);
            // validate if the message is cyphered
            if (cypherMasterKey) {
                /** @type {?} */
                var datablockLength = new DataView(new Uint8Array(messageFormmated.slice(1, 3)).buffer).getInt16(0, false);
                _this.cypherAesService.config(cypherMasterKey);
                /** @type {?} */
                var datablock = Array.from(_this.cypherAesService.decrypt((/** @type {?} */ (messageUnformated)).slice(3, datablockLength + 3)));
                // merge the datablock and the message
                messageFormmated = (/** @type {?} */ (messageUnformated))
                    .slice(0, 3)
                    .concat(datablock)
                    .concat((/** @type {?} */ (messageUnformated)).slice(-2));
            }
            _this._console.log('[BLE::Info] Notification reived from device: ', _this.cypherAesService.bytesTohex(messageFormmated));
            return messageFormmated;
        }), 
        // filter the message using the filter options
        filter(function (message) {
            /** @type {?} */
            var availableMessage = false;
            try {
                for (var filterOptions_1 = tslib_1.__values(filterOptions), filterOptions_1_1 = filterOptions_1.next(); !filterOptions_1_1.done; filterOptions_1_1 = filterOptions_1.next()) {
                    var option = filterOptions_1_1.value;
                    if (message[option.position] === option.byteToMatch) {
                        availableMessage = true;
                    }
                    else {
                        availableMessage = false;
                        break;
                    }
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (filterOptions_1_1 && !filterOptions_1_1.done && (_a = filterOptions_1.return)) _a.call(filterOptions_1);
                }
                finally { if (e_1) throw e_1.error; }
            }
            return availableMessage;
            var e_1, _a;
        }));
    };
    /**
     * Start a request to the browser to list all available bluetooth devices
     * @param {?=} options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @return {?}
     */
    BluetoothService.prototype.discoverDevice$ = /**
     * Start a request to the browser to list all available bluetooth devices
     * @param {?=} options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @return {?}
     */
    function (options) {
        var _this = this;
        if (options === void 0) { options = /** @type {?} */ ({}); }
        return defer(function () { return _this._webBle.requestDevice(options); }).pipe(mergeMap(function (device) {
            _this.device = device;
            _this._device$.emit(device);
            return _this.configureDeviceDisconnection$(device).pipe(mapTo(device));
        }));
    };
    /**
     * @param {?} device
     * @return {?}
     */
    BluetoothService.prototype.configureDeviceDisconnection$ = /**
     * @param {?} device
     * @return {?}
     */
    function (device) {
        var _this = this;
        return Observable.create(function (observer) {
            of(device)
                .pipe(mergeMap(function (dev) { return fromEvent(device, 'gattserverdisconnected'); }), take(1))
                .subscribe(function () {
                _this._console.log('Se desconecta disp en OnDevice disconnected!!!!!!!');
                _this.device = null;
                _this._device$.emit(null);
            }, function (err) {
                _this._console.log('[BLE::Info] Error in notifier: ', err);
                observer.error(err);
            }, function () {
            });
            observer.next("DisconnectionEvent as been register");
        });
    };
    /**
     * Discover all available devices and connect to a selected device
     * @param options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @returns the connected device
     */
    /**
     * Discover all available devices and connect to a selected device
     * @param {?=} options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @return {?} the connected device
     */
    BluetoothService.prototype.connectDevice$ = /**
     * Discover all available devices and connect to a selected device
     * @param {?=} options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @return {?} the connected device
     */
    function (options) {
        if (!options) {
            options = {
                acceptAllDevices: true,
                optionalServices: []
            };
        }
        else if (!options.optionalServices) {
            options.optionalServices = [];
        }
        options.optionalServices.push(GattServices.GENERIC_ACCESS.SERVICE);
        options.optionalServices.push(GattServices.BATTERY.SERVICE);
        options.optionalServices.push(GattServices.DEVICE_INFORMATION.SERVICE);
        return this.discoverDevice$(options).pipe(mergeMap(function (device) {
            return defer(function () { return (/** @type {?} */ (device)).gatt.connect(); });
        }));
    };
    /**
     * Disconnect the current device
     */
    /**
     * Disconnect the current device
     * @return {?}
     */
    BluetoothService.prototype.disconnectDevice = /**
     * Disconnect the current device
     * @return {?}
     */
    function () {
        if (this.device) {
            this._console.log('se deconecta dispositivo');
            this.device.gatt.disconnect();
        }
    };
    /**
     * get a data from the device using the characteristic
     * @param service UUID or GATT identifier service
     * @param characteristic UUID or GATT identifier characteristic
     * @return The characteristic data in a DataView object
     */
    /**
     * get a data from the device using the characteristic
     * @param {?} service UUID or GATT identifier service
     * @param {?} characteristic UUID or GATT identifier characteristic
     * @return {?} The characteristic data in a DataView object
     */
    BluetoothService.prototype.readDeviceValue$ = /**
     * get a data from the device using the characteristic
     * @param {?} service UUID or GATT identifier service
     * @param {?} characteristic UUID or GATT identifier characteristic
     * @return {?} The characteristic data in a DataView object
     */
    function (service, characteristic) {
        var _this = this;
        if (!this.device) {
            throw new Error('Must start a connection to a device before read the device value');
        }
        return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
            return _this.getCharacteristic$(primaryService, characteristic);
        }), mergeMap(function (characteristicValue) {
            return _this.readValue$(characteristicValue);
        }));
    };
    /**
     * write a value in the selected characteristic
     * @param characteristic the characterisitc where you want write the value
     * @param value value the value to write
     */
    /**
     * write a value in the selected characteristic
     * @param {?} service
     * @param {?} characteristic the characterisitc where you want write the value
     * @param {?} value value the value to write
     * @return {?}
     */
    BluetoothService.prototype.writeDeviceValue$ = /**
     * write a value in the selected characteristic
     * @param {?} service
     * @param {?} characteristic the characterisitc where you want write the value
     * @param {?} value value the value to write
     * @return {?}
     */
    function (service, characteristic, value) {
        var _this = this;
        if (!this.device) {
            throw new Error('Must start a connection to a device before read the device value');
        }
        return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
            return _this.getCharacteristic$(primaryService, characteristic);
        }), mergeMap(function (characteristicValue) {
            return _this.writeValue$(characteristicValue, value);
        }));
    };
    /**
     * get a primary service instance using the service UIID or GATT identifier
     * @param service service identifier
     * @return service instance
     */
    /**
     * get a primary service instance using the service UIID or GATT identifier
     * @param {?} service service identifier
     * @return {?} service instance
     */
    BluetoothService.prototype.getPrimaryService$ = /**
     * get a primary service instance using the service UIID or GATT identifier
     * @param {?} service service identifier
     * @return {?} service instance
     */
    function (service) {
        return of(this.device).pipe(mergeMap(function (device) {
            return device.gatt.getPrimaryService(service);
        }));
    };
    /**
     * Get a characterisitic instance using the service instance and a characteristic UUID
     * @param primaryService service instance
     * @param characteristic characterisitic identifier
     * @return characteristic instance
     */
    /**
     * Get a characterisitic instance using the service instance and a characteristic UUID
     * @param {?} primaryService service instance
     * @param {?} characteristic characterisitic identifier
     * @return {?} characteristic instance
     */
    BluetoothService.prototype.getCharacteristic$ = /**
     * Get a characterisitic instance using the service instance and a characteristic UUID
     * @param {?} primaryService service instance
     * @param {?} characteristic characterisitic identifier
     * @return {?} characteristic instance
     */
    function (primaryService, characteristic) {
        return defer(function () { return primaryService.getCharacteristic(characteristic); });
    };
    /**
     * read the characteristic value
     * @param {?} characteristic characteristic instance
     * @return {?} The characteristic data in a DataView object
     */
    BluetoothService.prototype.readValue$ = /**
     * read the characteristic value
     * @param {?} characteristic characteristic instance
     * @return {?} The characteristic data in a DataView object
     */
    function (characteristic) {
        return from(characteristic
            .readValue()
            .then(function (data) { return Promise.resolve(data); }, function (error) { return Promise.reject("" + error.message); }));
    };
    /**
     * write a value in the selected characteristic
     * @param {?} characteristic the characterisitc where you want write the value
     * @param {?} value the value to write
     * @return {?}
     */
    BluetoothService.prototype.writeValue$ = /**
     * write a value in the selected characteristic
     * @param {?} characteristic the characterisitc where you want write the value
     * @param {?} value the value to write
     * @return {?}
     */
    function (characteristic, value) {
        return defer(function () {
            return characteristic
                .writeValue(value);
        });
    };
    /**
     * change the state of the characteristic to enable it
     * @param service  parent service of the characteristic
     * @param characteristic characteristic to change the state
     * @param state new state
     */
    /**
     * change the state of the characteristic to enable it
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?=} state new state
     * @return {?}
     */
    BluetoothService.prototype.enableCharacteristic$ = /**
     * change the state of the characteristic to enable it
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?=} state new state
     * @return {?}
     */
    function (service, characteristic, state) {
        state = state || new Uint8Array([1]);
        return this.setCharacteristicState$(service, characteristic, state);
    };
    /**
     * change the state of the characteristic to disable it
     * @param service  parent service of the characteristic
     * @param characteristic characteristic to change the state
     * @param state new state
     */
    /**
     * change the state of the characteristic to disable it
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?=} state new state
     * @return {?}
     */
    BluetoothService.prototype.disbaleCharacteristic$ = /**
     * change the state of the characteristic to disable it
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?=} state new state
     * @return {?}
     */
    function (service, characteristic, state) {
        state = state || new Uint8Array([0]);
        return this.setCharacteristicState$(service, characteristic, state);
    };
    /**
     * set a state to an specific characteristic
     * @param service  parent service of the characteristic
     * @param characteristic characteristic to change the state
     * @param state new state
     * @return
     */
    /**
     * set a state to an specific characteristic
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?} state new state
     * @return {?}
     */
    BluetoothService.prototype.setCharacteristicState$ = /**
     * set a state to an specific characteristic
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?} state new state
     * @return {?}
     */
    function (service, characteristic, state) {
        var _this = this;
        /** @type {?} */
        var primaryService = this.getPrimaryService$(service);
        return primaryService.pipe(mergeMap(function (_primaryService) {
            return _this.getCharacteristic$(_primaryService, characteristic);
        }), map(function (_characteristic) {
            return _this.writeValue$(_characteristic, state);
        }));
    };
    /**
     * Send a message using a notifier characteristic
     * @param message message to send
     * @param service service to which the characteristic belongs
     * @param characteristic feature in which you want to send the notification
     */
    /**
     * Send a message using a notifier characteristic
     * @param {?} message message to send
     * @param {?} service service to which the characteristic belongs
     * @param {?} characteristic feature in which you want to send the notification
     * @return {?}
     */
    BluetoothService.prototype.sendToNotifier$ = /**
     * Send a message using a notifier characteristic
     * @param {?} message message to send
     * @param {?} service service to which the characteristic belongs
     * @param {?} characteristic feature in which you want to send the notification
     * @return {?}
     */
    function (message, service, characteristic) {
        var _this = this;
        return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
            return _this.getCharacteristic$(primaryService, characteristic);
        }), mergeMap(function (char) {
            if (message.length > 16) {
                /** @type {?} */
                var obsTest = of(undefined);
                while (message.length > 16) {
                    obsTest = obsTest.pipe(concat(_this.writeValue$(char, message.slice(0, 16))));
                    message = message.slice(16, message.length);
                }
                if (message.length > 0) {
                    obsTest = obsTest.pipe(concat(_this.writeValue$(char, message)));
                }
                return obsTest;
            }
            else {
                return _this.writeValue$(char, message);
            }
        }));
    };
    /**
     * The Battery Level characteristic is read using the GATT Read Characteristic
     *  Value sub-procedure and returns the current battery level as a percentage
     *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
     * represents a battery that is fully charged
     */
    /**
     * The Battery Level characteristic is read using the GATT Read Characteristic
     *  Value sub-procedure and returns the current battery level as a percentage
     *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
     * represents a battery that is fully charged
     * @return {?}
     */
    BluetoothService.prototype.getBatteryLevel$ = /**
     * The Battery Level characteristic is read using the GATT Read Characteristic
     *  Value sub-procedure and returns the current battery level as a percentage
     *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
     * represents a battery that is fully charged
     * @return {?}
     */
    function () {
        return this.readDeviceValue$(GattServices.BATTERY.SERVICE, GattServices.BATTERY.BATTERY_LEVEL).pipe(map(function (value) { return value.getUint8(0); }));
    };
    /**
     * This characteristic represents the name of the manufacturer of the device.
     */
    /**
     * This characteristic represents the name of the manufacturer of the device.
     * @return {?}
     */
    BluetoothService.prototype.getManufacturerName$ = /**
     * This characteristic represents the name of the manufacturer of the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.MANUFACTURER_NAME).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the model number that is assigned by the device vendor.
     */
    /**
     * This characteristic represents the model number that is assigned by the device vendor.
     * @return {?}
     */
    BluetoothService.prototype.getModelNumber$ = /**
     * This characteristic represents the model number that is assigned by the device vendor.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.MODEL_NUMBER).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the serial number for a particular instance of the device.
     */
    /**
     * This characteristic represents the serial number for a particular instance of the device.
     * @return {?}
     */
    BluetoothService.prototype.getSerialNumber$ = /**
     * This characteristic represents the serial number for a particular instance of the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SERIAL_NUMBER).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the hardware revision for the hardware within the device.
     */
    /**
     * This characteristic represents the hardware revision for the hardware within the device.
     * @return {?}
     */
    BluetoothService.prototype.getHardwareRevision$ = /**
     * This characteristic represents the hardware revision for the hardware within the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.HARDWARE_REVISION).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the firmware revision for the firmware within the device.
     */
    /**
     * This characteristic represents the firmware revision for the firmware within the device.
     * @return {?}
     */
    BluetoothService.prototype.getFirmwareRevision$ = /**
     * This characteristic represents the firmware revision for the firmware within the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.FIRMWARE_REVISION).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the software revision for the software within the device.
     */
    /**
     * This characteristic represents the software revision for the software within the device.
     * @return {?}
     */
    BluetoothService.prototype.getSoftwareRevision$ = /**
     * This characteristic represents the software revision for the software within the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SOFTWARE_REVISION).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents a structure containing an Organizationally Unique Identifier
     *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
     */
    /**
     * This characteristic represents a structure containing an Organizationally Unique Identifier
     *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
     * @return {?}
     */
    BluetoothService.prototype.getSystemId$ = /**
     * This characteristic represents a structure containing an Organizationally Unique Identifier
     *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
     * @return {?}
     */
    function () {
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SYSTEM_ID);
    };
    /**
     * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
     */
    /**
     * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
     * @return {?}
     */
    BluetoothService.prototype.getPnpId$ = /**
     * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
     * @return {?}
     */
    function () {
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.PNP_ID);
    };
    BluetoothService.decorators = [
        { type: Injectable, args: [{
                    providedIn: 'root'
                },] },
    ];
    /** @nocollapse */
    BluetoothService.ctorParameters = function () { return [
        { type: BrowserWebBluetooth },
        { type: CypherAesService },
        { type: ConsoleLoggerService }
    ]; };
    /** @nocollapse */ BluetoothService.ngInjectableDef = i0.defineInjectable({ factory: function BluetoothService_Factory() { return new BluetoothService(i0.inject(i1.BrowserWebBluetooth), i0.inject(i2.CypherAesService), i0.inject(i3.ConsoleLoggerService)); }, token: BluetoothService, providedIn: "root" });
    return BluetoothService;
}(Subject));
export { BluetoothService };
if (false) {
    /** @type {?} */
    BluetoothService.prototype._device$;
    /** @type {?} */
    BluetoothService.prototype.device;
    /** @type {?} */
    BluetoothService.prototype.serviceCharacteristicVsSubscriptionList;
    /** @type {?} */
    BluetoothService.prototype.notifierSubject;
    /** @type {?} */
    BluetoothService.prototype.notifierStartedSubject;
    /** @type {?} */
    BluetoothService.prototype.bluetoothAvailable;
    /** @type {?} */
    BluetoothService.prototype._webBle;
    /** @type {?} */
    BluetoothService.prototype.cypherAesService;
    /** @type {?} */
    BluetoothService.prototype._console;
}

//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmx1ZXRvb3RoLnNlcnZpY2UuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9AbmVidWxhZS9hbmd1bGFyLWJsZS8iLCJzb3VyY2VzIjpbImxpYi9ibHVldG9vdGgvYmx1ZXRvb3RoLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RCxPQUFPLEVBQ0wsR0FBRyxFQUNILFFBQVEsRUFDUixNQUFNLEVBQ04sS0FBSyxFQUNMLE1BQU0sRUFDTixTQUFTLEVBRVQsSUFBSSxFQUNKLEdBQUcsRUFDSCxJQUFJLEVBQ0osT0FBTyxFQUNQLFNBQVMsRUFDVCxLQUFLLEVBQ04sTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQ0wsVUFBVSxFQUNWLFFBQVEsRUFDUixPQUFPLEVBQ1AsS0FBSyxFQUNMLFNBQVMsRUFDVCxFQUFFLEVBQ0YsSUFBSSxFQUVMLE1BQU0sTUFBTSxDQUFDO0FBQ2QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzFELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG1CQUFtQixDQUFDOzs7Ozs7SUFLbkIsNENBQXlCO0lBTzdELDBCQUNTLFNBQ0Msa0JBQ0Q7UUFIVCxZQUtFLGlCQUFPLFNBS1I7UUFUUSxhQUFPLEdBQVAsT0FBTztRQUNOLHNCQUFnQixHQUFoQixnQkFBZ0I7UUFDakIsY0FBUSxHQUFSLFFBQVE7d0RBUGlDLEVBQUU7Z0NBQzFCLElBQUksT0FBTyxFQUFFO3VDQUNOLElBQUksT0FBTyxFQUFFO21DQUNqQixLQUFLO1FBT2hDLEtBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxZQUFZLEVBQW1CLENBQUM7UUFDcEQsRUFBRSxDQUFD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SDtJQUNEOztPQUVHOzs7OztJQUNILCtDQUFvQjs7OztJQUFwQjtRQUFBLGlCQVNDO1FBUkMsTUFBTSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FDMUIsWUFBWSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sRUFDdkMsWUFBWSxDQUFDLGtCQUFrQixDQUFDLGlCQUFpQixDQUNsRCxDQUFDLElBQUksQ0FDSixHQUFHLENBQUMsVUFBQSxRQUFRO1lBQ1YsT0FBQSxLQUFJLENBQUMsZ0JBQWdCLENBQUMsV0FBVyxDQUFDLElBQUksVUFBVSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUFsRSxDQUFrRSxDQUNuRSxDQUNGLENBQUM7S0FDSDtJQUNEOzs7T0FHRzs7Ozs7O0lBQ0gsdUNBQVk7Ozs7O0lBQVo7UUFDRSxNQUFNLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUMxQixZQUFZLENBQUMsa0JBQWtCLENBQUMsT0FBTyxFQUN2QyxZQUFZLENBQUMsa0JBQWtCLENBQUMsU0FBUyxDQUMxQyxDQUFDO0tBQ0g7SUFDRDs7T0FFRzs7Ozs7SUFDSCxvQ0FBUzs7OztJQUFUO1FBQ0UsTUFBTSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FDMUIsWUFBWSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sRUFDdkMsWUFBWSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FDdkMsQ0FBQztLQUNIOztnQkEvcEJGLFVBQVUsU0FBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkI7Ozs7Z0JBTlEsbUJBQW1CO2dCQUNuQixnQkFBZ0I7Z0JBQ2hCLG9CQUFvQjs7OzJCQTdCN0I7RUFrQ3NDLE9BQU87U0FBaEMsZ0JBQWdCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgRXZlbnRFbWl0dGVyIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBtYXAsXG4gIG1lcmdlTWFwLFxuICBjb25jYXQsXG4gIG1hcFRvLFxuICBmaWx0ZXIsXG4gIHRha2VVbnRpbCxcbiAgYnVmZmVyVG9nZ2xlLFxuICBzY2FuLFxuICB0YXAsXG4gIHRha2UsXG4gIHRpbWVvdXQsXG4gIHJldHJ5V2hlbixcbiAgZGVsYXlcbn0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHtcbiAgT2JzZXJ2YWJsZSxcbiAgZm9ya0pvaW4sXG4gIFN1YmplY3QsXG4gIGRlZmVyLFxuICBmcm9tRXZlbnQsXG4gIG9mLFxuICBmcm9tLFxuICBpbnRlcnZhbFxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEdhdHRTZXJ2aWNlcyB9IGZyb20gJy4vZ2F0dC1zZXJ2aWNlcyc7XG5pbXBvcnQgeyBCcm93c2VyV2ViQmx1ZXRvb3RoIH0gZnJvbSAnLi4vcGxhdGZvcm0vYnJvd3Nlcic7XG5pbXBvcnQgeyBDeXBoZXJBZXNTZXJ2aWNlIH0gZnJvbSAnLi4vY3lwaGVyL2N5cGhlci1hZXMuc2VydmljZSc7XG5pbXBvcnQgeyBDb25zb2xlTG9nZ2VyU2VydmljZSB9IGZyb20gJy4uL2xvZ2dlci5zZXJ2aWNlJztcblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgQmx1ZXRvb3RoU2VydmljZSBleHRlbmRzIFN1YmplY3Q8Qmx1ZXRvb3RoU2VydmljZT4ge1xuICBwdWJsaWMgX2RldmljZSQ6IEV2ZW50RW1pdHRlcjxCbHVldG9vdGhEZXZpY2U+O1xuICBwcml2YXRlIGRldmljZTogQmx1ZXRvb3RoRGV2aWNlO1xuICBwcml2YXRlIHNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdCA9IHt9O1xuICBwcml2YXRlIG5vdGlmaWVyU3ViamVjdCA9IG5ldyBTdWJqZWN0KCk7XG4gIHByaXZhdGUgbm90aWZpZXJTdGFydGVkU3ViamVjdCA9IG5ldyBTdWJqZWN0KCk7XG4gIHByaXZhdGUgYmx1ZXRvb3RoQXZhaWxhYmxlID0gZmFsc2U7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHB1YmxpYyBfd2ViQmxlOiBCcm93c2VyV2ViQmx1ZXRvb3RoLFxuICAgIHByaXZhdGUgY3lwaGVyQWVzU2VydmljZTogQ3lwaGVyQWVzU2VydmljZSxcbiAgICBwdWJsaWMgX2NvbnNvbGU6IENvbnNvbGVMb2dnZXJTZXJ2aWNlXG4gICkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5fZGV2aWNlJCA9IG5ldyBFdmVudEVtaXR0ZXI8Qmx1ZXRvb3RoRGV2aWNlPigpO1xuICAgIGlmIChfd2ViQmxlLl9ibGUpIHtcbiAgICAgIHRoaXMuYmx1ZXRvb3RoQXZhaWxhYmxlID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBpc0JsdWV0b290aEF2YWlsYWJsZSgpIHtcbiAgICByZXR1cm4gdGhpcy5ibHVldG9vdGhBdmFpbGFibGU7XG4gIH1cbiAgLyoqXG4gICAqIGdldCB0aGUgY3VycmVudCBkZXZpY2UsIGlmIHRoZSBkZXZpY2UgcmV0dXJuIG51bGwgaXMgYmVjYXVzZSB0aGUgY29ubmVjdGlvbiBoYXMgbG9zdFxuICAgKiBAcmV0dXJucyB0aGUgY3VycmVudCBjb25uY2V0ZWQgZGV2aWNlXG4gICAqL1xuICBnZXREZXZpY2UkKCk6IE9ic2VydmFibGU8Qmx1ZXRvb3RoRGV2aWNlPiB7XG4gICAgcmV0dXJuIHRoaXMuX2RldmljZSQ7XG4gIH1cblxuICBnZXROb3RpZmllclN0YXJ0ZWRTdWJqZWN0JCgpIHtcbiAgICByZXR1cm4gdGhpcy5ub3RpZmllclN0YXJ0ZWRTdWJqZWN0O1xuICB9XG4gIC8qKlxuICAgKiBzdGFydCBhIHN0cmVhbSBieSBub3RpZmllcnMgY2hhcmFjdGVyaXN0aWNzXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFRoZSBzZXJ2aWNlIHRvIHdoaWNoIHRoZSBjaGFyYWN0ZXJpc3RpYyBiZWxvbmdzXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBUaGUgY2hhcmFjdGVyaXN0aWMgd2hvc2UgdmFsdWUgeW91IHdhbnQgdG8gbGlzdGVuXG4gICAqIEBwYXJhbSBvcHRpb25zIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZVxuICAgKiBzdGFydEJ5dGU6bnVtYmVyIChyZXF1aXJlZCksIHN0b3BCeXRlOm51bWJlciAocmVxdWlyZWQpLFxuICAgKiBsZW5ndGhQb3NpdGlvbjogeyBzdGFydDogbnVtYmVyLCBlbmQ6IG51bWJlciwgbGVuZ3RoUGFkZGluZzogbnVtYmVyIH0gKHJlcXVpcmVkKVxuICAgKiBAcmV0dXJucyBBIERhdGFWaWV3IHRoYW4gY29udGFpbnMgdGhlIGNoYXJhY3RlcmlzdGljIHZhbHVlXG4gICAqL1xuICBzdGFydE5vdGlmaWVyTGlzdGVuZXIkKHNlcnZpY2UsIGNoYXJhY3RlcmlzdGljLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIGRlZmVyKCgpID0+IHtcbiAgICAgIG9mKHNlcnZpY2UpXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIHRhcCgoKSA9PiB0aGlzLl9jb25zb2xlLmxvZygnSW5pY2lhIGVsIG5vdGlmaWVyIGNvbiBsYSBpbnN0YW5jaWE6ICcsIHRoaXMuc2VydmljZUNoYXJhY3RlcmlzdGljVnNTdWJzY3JpcHRpb25MaXN0KSksXG4gICAgICAgICAgZmlsdGVyKFxuICAgICAgICAgICAgXyA9PlxuICAgICAgICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdCkuaW5kZXhPZihcbiAgICAgICAgICAgICAgICBgJHtzZXJ2aWNlfS0ke2NoYXJhY3RlcmlzdGljfWBcbiAgICAgICAgICAgICAgKSA9PT0gLTFcbiAgICAgICAgICApXG4gICAgICAgIClcbiAgICAgICAgLnN1YnNjcmliZShcbiAgICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdFtgJHtzZXJ2aWNlfS0ke2NoYXJhY3RlcmlzdGljfWBdID1cbiAgICAgICAgICAgICAgdGhpcy5idWlsZE5vdGlmaWVyTGlzdGVuZXIkKFxuICAgICAgICAgICAgICAgIHNlcnZpY2UsXG4gICAgICAgICAgICAgICAgY2hhcmFjdGVyaXN0aWMsXG4gICAgICAgICAgICAgICAgb3B0aW9uc1xuICAgICAgICAgICAgICApLnN1YnNjcmliZShtZXNzYWdlID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLm5vdGlmaWVyU3ViamVjdC5uZXh0KG1lc3NhZ2UpO1xuICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgIH0sXG4gICAgICAgICAgZXJyID0+IHtcbiAgICAgICAgICAgIHRoaXMuX2NvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBFcnJvciBpbiBub3RpZmllcjogJywgZXJyKTtcbiAgICAgICAgICB9LFxuICAgICAgICAgICgpID0+IHsgfVxuICAgICAgKTtcbiAgICAgIHJldHVybiBvZihgbm90aWZpZXIgYXMgc3Vic2NyaWJlZDogc2VydmljZT0gJHtzZXJ2aWNlfSwgY2hhcmFjdGVyaXN0aWM9ICR7Y2hhcmFjdGVyaXN0aWN9YCk7XG4gICAgfSk7XG5cbiAgfVxuXG4gIHN0b3BOb3RpZmllckxpc3RlbmVyJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYykge1xuICAgIHJldHVybiBkZWZlcigoKSA9PiB7XG4gICAgICAvLyB0aGlzLnNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdFtgJHtzZXJ2aWNlfS0ke2NoYXJhY3RlcmlzdGljfWBdLnVuc3Vic2NyaWJlKCk7XG4gICAgICBkZWxldGUgdGhpcy5zZXJ2aWNlQ2hhcmFjdGVyaXN0aWNWc1N1YnNjcmlwdGlvbkxpc3RbYCR7c2VydmljZX0tJHtjaGFyYWN0ZXJpc3RpY31gXTtcbiAgICAgIHJldHVybiBvZihgdGhlIG5vdGlmaWVyIG9mIHRoZSBjaGFyYWN0ZXJpc3RpYyAke2NoYXJhY3RlcmlzdGljfSBhcyBiZWVuIHN0b3BwZWRgKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYnVpbGROb3RpZmllckxpc3RlbmVyJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgb3B0aW9ucykge1xuICAgIHJldHVybiB0aGlzLmdldFByaW1hcnlTZXJ2aWNlJChzZXJ2aWNlKS5waXBlKFxuICAgICAgdGFwKHNlcnZpY2VJbnN0YW5jZSA9PiB0aGlzLl9jb25zb2xlLmxvZyhgdG9tYSBleGl0b3NhbWVudGUgZWwgc2VydmljaW8gPT09PT09PT09PT09PT09PT4gJHtzZXJ2aWNlSW5zdGFuY2V9YCkpLFxuICAgICAgbWVyZ2VNYXAocHJpbWFyeVNlcnZpY2UgPT5cbiAgICAgICAgdGhpcy5nZXRDaGFyYWN0ZXJpc3RpYyQocHJpbWFyeVNlcnZpY2UsIGNoYXJhY3RlcmlzdGljKVxuICAgICAgICAucGlwZSh0YXAoY2hhciA9PiB0aGlzLl9jb25zb2xlLmxvZyhgdG9tYSBleGl0b3NhbWVudGUgbGEgY2FyYWN0ZXJpc3RpY2EgPT09PT09PT09PT09PT09PT4gJHtjaGFyfWApKSlcbiAgICAgICksXG4gICAgICBtZXJnZU1hcCgoY2hhcjogQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljKSA9PiB7XG4gICAgICAgIHJldHVybiBkZWZlcigoKSA9PiB7XG4gICAgICAgICAgLy8gZW5hYmxlIHRoZSBjaGFyYWN0ZXJpc3RpYyBub3RpZmllclxuICAgICAgICAgIHJldHVybiBjaGFyLnN0YXJ0Tm90aWZpY2F0aW9ucygpO1xuICAgICAgICB9KS5waXBlKFxuICAgICAgICAgIHJldHJ5V2hlbihlcnJvciA9PlxuICAgICAgICAgICAgZXJyb3IucGlwZShcbiAgICAgICAgICAgICAgdGFwKCgpID0+IHRoaXMuX2NvbnNvbGUubG9nKCdFUlJPUiBFTiBFTCBzdGFydE5vdGlmaWNhdGlvbnMgPT09PT09PT09PT09PT09PT4gJykpLFxuICAgICAgICAgICAgICBkZWxheSgxMDAwKSxcbiAgICAgICAgICAgICAgdGFrZSg1KVxuICAgICAgICAgICAgKVxuICAgICAgICAgICksXG4gICAgICAgICAgdGFwKCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMubm90aWZpZXJTdGFydGVkU3ViamVjdC5uZXh0KHRydWUpO1xuICAgICAgICAgICAgdGhpcy5fY29uc29sZS5sb2coYGluY2lhIGxhcyBub3RpZmlhY2lvbmVzIGRlIGxhIGNhcmFjdGVyaXN0aWNhIDIgPT09PT09PT09PT09PT09PT4gJHtjaGFyYWN0ZXJpc3RpY31gKTtcbiAgICAgICAgICB9KSxcbiAgICAgICAgICBtZXJnZU1hcChfID0+IHtcbiAgICAgICAgICAgIC8vIHN0YXJ0IHRoZSBsaXN0ZXIgZnJvbSB0aGUgZXZlbiBjaGFyYWN0ZXJpc3RpY3ZhbHVlY2hhbmdlZCB0byBnZXQgYWxsIGNoYW5nZXMgb24gdGhlIHNwZWNpZmljXG4gICAgICAgICAgICAvLyBjaGFyYWN0ZXJpc3RpY1xuICAgICAgICAgICAgcmV0dXJuIGZyb21FdmVudChjaGFyLCAnY2hhcmFjdGVyaXN0aWN2YWx1ZWNoYW5nZWQnKS5waXBlKFxuICAgICAgICAgICAgICB0YWtlVW50aWwoZnJvbUV2ZW50KGNoYXIsICdnYXR0c2VydmVyZGlzY29ubmVjdGVkJykpLFxuICAgICAgICAgICAgICBtYXAoKGV2ZW50OiBFdmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIGdldCBhIGV2ZW50IGZyb20gdGhlIGNoYXJhY3RlcmlzdGljIGFuZCBtYXAgdGhhdFxuICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICBzdGFydEJ5dGVNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIHN0b3BCeXRlTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICBsZW5ndGhNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIG1lc3NhZ2VMZW5ndGg6IDAsXG4gICAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IERhdGUubm93KCksXG4gICAgICAgICAgICAgICAgICBkYXRhOiBBcnJheS5mcm9tKFxuICAgICAgICAgICAgICAgICAgICBuZXcgVWludDhBcnJheShcbiAgICAgICAgICAgICAgICAgICAgICAoKGV2ZW50LnRhcmdldCBhcyBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMpXG4gICAgICAgICAgICAgICAgICAgICAgICAudmFsdWUgYXMgRGF0YVZpZXcpLmJ1ZmZlclxuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgIHNjYW4oXG4gICAgICAgICAgICAgICAgKGFjYywgdmFsdWUpID0+IHtcbiAgICAgICAgICAgICAgICAgIGFjYy50aW1lc3RhbXAgPSBhY2MudGltZXN0YW1wID09PSAwID8gdmFsdWUudGltZXN0YW1wIDogYWNjLnRpbWVzdGFtcDtcbiAgICAgICAgICAgICAgICAgIC8vIGlmIHRoZSBjdXJyZW50IGFjY3VtdWxhdG9yIHZhbHVlIGlzIGEgdmFsaWQgbWVzc2FnZSwgdGhlbiBpcyByZXN0YXJ0ZWQgdG8gZ2V0IHRoZSBuZXh0XG4gICAgICAgICAgICAgICAgICAvLyBtZXNzYWdlXG4gICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgIChhY2MubGVuZ3RoTWF0Y2hlcyAmJlxuICAgICAgICAgICAgICAgICAgICAgIGFjYy5zdGFydEJ5dGVNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICAgICAgYWNjLnN0b3BCeXRlTWF0Y2hlcylcbiAgICAgICAgICAgICAgICAgICAgfHwgYWNjLnRpbWVzdGFtcCArIDEwMDAgPCBEYXRlLm5vdygpXG4gICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgYWNjID0ge1xuICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0Qnl0ZU1hdGNoZXM6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgIHN0b3BCeXRlTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZUxlbmd0aDogMCxcbiAgICAgICAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IDAsXG4gICAgICAgICAgICAgICAgICAgICAgZGF0YTogW11cbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIC8vIHZhbGlkYXRlIHRoZSBzdGFydCBieXRlXG4gICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgICFhY2Muc3RhcnRCeXRlTWF0Y2hlcyAmJlxuICAgICAgICAgICAgICAgICAgICB2YWx1ZS5kYXRhWzBdID09PSBvcHRpb25zLnN0YXJ0Qnl0ZVxuICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGdldCB0aGUgbWVzc2FnZSBsZW5ndGggdXNpbmcgdGhlIHN0YXJ0IGFuZCBlbmQgcG9zaXRpb25cbiAgICAgICAgICAgICAgICAgICAgYWNjLm1lc3NhZ2VMZW5ndGggPVxuICAgICAgICAgICAgICAgICAgICAgIG5ldyBEYXRhVmlldyhcbiAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBVaW50OEFycmF5KFxuICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5kYXRhLnNsaWNlKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdGlvbnMubGVuZ3RoUG9zaXRpb24uc3RhcnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5sZW5ndGhQb3NpdGlvbi5lbmRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgICAgICAgICAgKS5idWZmZXJcbiAgICAgICAgICAgICAgICAgICAgICApLmdldEludDE2KDAsIGZhbHNlKSArXG4gICAgICAgICAgICAgICAgICAgICAgKG9wdGlvbnMubGVuZ3RoUG9zaXRpb24ubGVuZ3RoUGFkZGluZ1xuICAgICAgICAgICAgICAgICAgICAgICAgPyBvcHRpb25zLmxlbmd0aFBvc2l0aW9uLmxlbmd0aFBhZGRpbmdcbiAgICAgICAgICAgICAgICAgICAgICAgIDogMCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIHZhbGlkIHRoYXQgdGhlIGluaXRpYWwgYnl0ZSB3YXMgZm91bmRcbiAgICAgICAgICAgICAgICAgICAgYWNjLnN0YXJ0Qnl0ZU1hdGNoZXMgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgICAhYWNjLnN0b3BCeXRlTWF0Y2hlcyAmJlxuICAgICAgICAgICAgICAgICAgICB2YWx1ZS5kYXRhW3ZhbHVlLmRhdGEubGVuZ3RoIC0gMV0gPT09IG9wdGlvbnMuc3RvcEJ5dGVcbiAgICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgICAvLyB2YWxpZCB0aGF0IHRoZSBlbmQgYnl0ZSB3YXMgZm91bmRcbiAgICAgICAgICAgICAgICAgICAgYWNjLnN0b3BCeXRlTWF0Y2hlcyA9IHRydWU7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBpZiAoYWNjLnN0YXJ0Qnl0ZU1hdGNoZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gbWVyZ2UgdGhlIG5ldyBkYXRhIGJ5dGVzIHRvIHRoZSBvbGQgYnl0ZXNcbiAgICAgICAgICAgICAgICAgICAgYWNjLmRhdGEgPSBhY2MuZGF0YS5jb25jYXQodmFsdWUuZGF0YSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBhY2MubGVuZ3RoTWF0Y2hlcyA9XG4gICAgICAgICAgICAgICAgICAgIGFjYy5zdGFydEJ5dGVNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICAgIGFjYy5zdG9wQnl0ZU1hdGNoZXMgJiZcbiAgICAgICAgICAgICAgICAgICAgYWNjLm1lc3NhZ2VMZW5ndGggPT09IGFjYy5kYXRhLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBhY2M7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBzdGFydEJ5dGVNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIHN0b3BCeXRlTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICBsZW5ndGhNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIG1lc3NhZ2VMZW5ndGg6IDAsXG4gICAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IDAsXG4gICAgICAgICAgICAgICAgICBkYXRhOiBbXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgLy8gb25seSBwdWJsaXNoIHRoZSBjb21wbGV0ZSBhbmQgdmFsaWQgbWVzc2FnZVxuICAgICAgICAgICAgICBmaWx0ZXIoXG4gICAgICAgICAgICAgICAgZGF0YSA9PlxuICAgICAgICAgICAgICAgICAgZGF0YS5sZW5ndGhNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICBkYXRhLnN0YXJ0Qnl0ZU1hdGNoZXMgJiZcbiAgICAgICAgICAgICAgICAgIGRhdGEuc3RvcEJ5dGVNYXRjaGVzXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgIC8vIHJlbW92ZSBhbGwgY3VzdG9tIGRhdGEgYW5kIHB1Ymxpc2ggdGhlIG1lc3NhZ2UgZGF0YVxuICAgICAgICAgICAgICBtYXAocmVzdWx0ID0+IHJlc3VsdC5kYXRhKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9KVxuICAgICAgICApO1xuICAgICAgfSlcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBTZW5kIGEgcmVxdWVzdCB0byB0aGUgZGV2aWNlIGFuZCB3YWl0IGEgdW5pcXVlIHJlc3BvbnNlXG4gICAqIEBwYXJhbSBtZXNzYWdlIE1lc3NhZ2UgdG8gc2VuZFxuICAgKiBAcGFyYW0gc2VydmljZSBUaGUgc2VydmljZSB0byB3aGljaCB0aGUgY2hhcmFjdGVyaXN0aWMgYmVsb25nc1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVGhlIGNoYXJhY3RlcmlzdGljIHdob3NlIHZhbHVlIHlvdSB3YW50IHRvIHNlbmQgdGhlIG1lc3NhZ2VcbiAgICogQHBhcmFtIHJlc3BvbnNlVHlwZSBmaWx0ZXIgdG8gdXNlIHRvIGlkZW50aWZ5IHRoZSByZXNwb25zZSwgU2FtcGxlOiBbe3Bvc2l0aW9uOiAzLCBieXRlVG9NYXRjaDogMHg4M30sXG4gICAqICB7cG9zaXRpb246IDEzLCBieXRlVG9NYXRjaDogMHg0NX1dXG4gICAqIEBwYXJhbSBjeXBoZXJNYXN0ZXJLZXkgbWFzdGVyIGtleSB0byBkZWNyeXB0IHRoZSBtZXNzYWdlLCBvbmx5IHVzZSB0aGlzIHBhcmEgaWYgdGhlIG1lc3NhZ2UgdG8gcmVjZWl2ZSBpcyBlbmNyeXB0ZWRcbiAgICovXG4gIHNlbmRBbmRXYWl0UmVzcG9uc2UkKFxuICAgIG1lc3NhZ2UsXG4gICAgc2VydmljZSxcbiAgICBjaGFyYWN0ZXJpc3RpYyxcbiAgICByZXNwb25zZVR5cGUsXG4gICAgY3lwaGVyTWFzdGVyS2V5P1xuICApIHtcbiAgICB0aGlzLl9jb25zb2xlLmxvZyhcbiAgICAgICdbQkxFOjpJbmZvXSBTZW5kIG1lc3NhZ2UgdG8gZGV2aWNlOiAnLFxuICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9oZXgobWVzc2FnZSlcbiAgICApO1xuICAgIHJldHVybiBmb3JrSm9pbihcbiAgICAgIHRoaXMuc3Vic2NyaWJlVG9Ob3RpZmllckxpc3RlbmVyKHJlc3BvbnNlVHlwZSwgY3lwaGVyTWFzdGVyS2V5KS5waXBlKFxuICAgICAgICB0YWtlKDEpXG4gICAgICApLFxuICAgICAgdGhpcy5zZW5kVG9Ob3RpZmllciQobWVzc2FnZSwgc2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgKS5waXBlKFxuICAgICAgbWFwKChbbWVzc2FnZVJlc3AsIF9dKSA9PiBtZXNzYWdlUmVzcCksXG4gICAgICB0aW1lb3V0KDMwMDApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogU3Vic2NyaWJlIHRvIHRoZSBub3RpZmllcnMgZmlsdGVyaW5nIGJ5IGJ5dGUgY2hlY2tpbmdcbiAgICogQHBhcmFtIGZpbHRlck9wdGlvbnMgbXVzdCBzcGVjaWZpYyB0aGUgcG9zaXRpb24gYW5kIHRoZSBieXRlIHRvIG1hdGNoIFNhbXBsZTpcbiAgICogW3twb3NpdGlvbjogMywgYnl0ZVRvTWF0Y2g6IDB4ODN9LCB7cG9zaXRpb246IDEzLCBieXRlVG9NYXRjaDogMHg0NX1dXG4gICAqIEBwYXJhbSBjeXBoZXJNYXN0ZXJLZXkgbWFzdGVyIGtleSB0byBkZWNyeXB0IHRoZSBtZXNzYWdlLCBvbmx5IHVzZSB0aGlzIHBhcmEgaWYgdGhlIG1lc3NhZ2UgdG8gcmVjZWl2ZSBpcyBlbmNyeXB0ZWRcbiAgICovXG4gIHN1YnNjcmliZVRvTm90aWZpZXJMaXN0ZW5lcihmaWx0ZXJPcHRpb25zLCBjeXBoZXJNYXN0ZXJLZXk/KSB7XG4gICAgcmV0dXJuIHRoaXMubm90aWZpZXJTdWJqZWN0LnBpcGUoXG4gICAgICBtYXAobWVzc2FnZVVuZm9ybWF0ZWQgPT4ge1xuICAgICAgICAvLyByZWNlaXZlIHRoZSBwdXJlIG1lc3NhZ2VcbiAgICAgICAgbGV0IG1lc3NhZ2VGb3JtbWF0ZWQgPSBtZXNzYWdlVW5mb3JtYXRlZCBhcyBhbnk7XG4gICAgICAgIC8vIHZhbGlkYXRlIGlmIHRoZSBtZXNzYWdlIGlzIGN5cGhlcmVkXG4gICAgICAgIGlmIChjeXBoZXJNYXN0ZXJLZXkpIHtcbiAgICAgICAgICAvLyBnZXQgdGhlIG1lc3NhZ2VMZW5ndGhcbiAgICAgICAgICBjb25zdCBkYXRhYmxvY2tMZW5ndGggPSBuZXcgRGF0YVZpZXcoXG4gICAgICAgICAgICBuZXcgVWludDhBcnJheShtZXNzYWdlRm9ybW1hdGVkLnNsaWNlKDEsIDMpKS5idWZmZXJcbiAgICAgICAgICApLmdldEludDE2KDAsIGZhbHNlKTtcbiAgICAgICAgICB0aGlzLmN5cGhlckFlc1NlcnZpY2UuY29uZmlnKGN5cGhlck1hc3RlcktleSk7XG4gICAgICAgICAgLy8gZ2V0IHRoZSBkYXRhYmxvY2sgb2YgdGhlIG1lc3NhZ2UgYW5kIGRlY3J5cHQgdGhpcyBzZWN0aW9uXG4gICAgICAgICAgY29uc3QgZGF0YWJsb2NrID0gQXJyYXkuZnJvbShcbiAgICAgICAgICAgIHRoaXMuY3lwaGVyQWVzU2VydmljZS5kZWNyeXB0KFxuICAgICAgICAgICAgICAobWVzc2FnZVVuZm9ybWF0ZWQgYXMgYW55KS5zbGljZSgzLCBkYXRhYmxvY2tMZW5ndGggKyAzKVxuICAgICAgICAgICAgKVxuICAgICAgICAgICk7XG4gICAgICAgICAgLy8gbWVyZ2UgdGhlIGRhdGFibG9jayBhbmQgdGhlIG1lc3NhZ2VcbiAgICAgICAgICBtZXNzYWdlRm9ybW1hdGVkID0gKG1lc3NhZ2VVbmZvcm1hdGVkIGFzIGFueSlcbiAgICAgICAgICAgIC5zbGljZSgwLCAzKVxuICAgICAgICAgICAgLmNvbmNhdChkYXRhYmxvY2spXG4gICAgICAgICAgICAuY29uY2F0KChtZXNzYWdlVW5mb3JtYXRlZCBhcyBhbnkpLnNsaWNlKC0yKSk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fY29uc29sZS5sb2coXG4gICAgICAgICAgJ1tCTEU6OkluZm9dIE5vdGlmaWNhdGlvbiByZWl2ZWQgZnJvbSBkZXZpY2U6ICcsXG4gICAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9oZXgobWVzc2FnZUZvcm1tYXRlZClcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2VGb3JtbWF0ZWQ7XG4gICAgICB9KSxcbiAgICAgIC8vIGZpbHRlciB0aGUgbWVzc2FnZSB1c2luZyB0aGUgZmlsdGVyIG9wdGlvbnNcbiAgICAgIGZpbHRlcihtZXNzYWdlID0+IHtcbiAgICAgICAgbGV0IGF2YWlsYWJsZU1lc3NhZ2UgPSBmYWxzZTtcbiAgICAgICAgZm9yIChjb25zdCBvcHRpb24gb2YgZmlsdGVyT3B0aW9ucykge1xuICAgICAgICAgIGlmIChtZXNzYWdlW29wdGlvbi5wb3NpdGlvbl0gPT09IG9wdGlvbi5ieXRlVG9NYXRjaCkge1xuICAgICAgICAgICAgYXZhaWxhYmxlTWVzc2FnZSA9IHRydWU7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGF2YWlsYWJsZU1lc3NhZ2UgPSBmYWxzZTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXZhaWxhYmxlTWVzc2FnZTtcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCBhIHJlcXVlc3QgdG8gdGhlIGJyb3dzZXIgdG8gbGlzdCBhbGwgYXZhaWxhYmxlIGJsdWV0b290aCBkZXZpY2VzXG4gICAqIEBwYXJhbSBvcHRpb25zIE9wdGlvbnMgdG8gcmVxdWVzdCB0aGUgZGV2aWNlcyB0aGUgc3RydWN0dXJlIGlzOlxuICAgKiBhY2NlcHRBbGxEZXZpY2VzOiB0cnVlfGZhbHNlXG4gICAqIGZpbHRlcnM6IEJsdWV0b290aERhdGFGaWx0ZXJJbml0IChzZWUgaHR0cHM6Ly93ZWJibHVldG9vdGhjZy5naXRodWIuaW8vd2ViLWJsdWV0b290aC8jZGljdGRlZi1ibHVldG9vdGhsZXNjYW5maWx0ZXJpbml0IGZvciBtb3JlIGluZm8pXG4gICAqIG9wdGlvbmFsU2VydmljZXM6IFtdIChzZXJ2aWNlcyB0aGF0IGFyZSBnb2luZyB0byBiZSB1c2VkIGluXG4gICAqIGNvbW11bmljYXRpb24gd2l0aCB0aGUgZGV2aWNlLCBtdXN0IHVzZSB0aGUgVUlJRCBvciBHQVRUIGlkZW50ZmllciB0byBsaXN0IHRoZXIgc2VydmljZXMpXG4gICAqL1xuICBwcml2YXRlIGRpc2NvdmVyRGV2aWNlJChcbiAgICBvcHRpb25zOiBSZXF1ZXN0RGV2aWNlT3B0aW9ucyA9IHt9IGFzIFJlcXVlc3REZXZpY2VPcHRpb25zXG4gICkge1xuICAgIHJldHVybiBkZWZlcigoKSA9PiB0aGlzLl93ZWJCbGUucmVxdWVzdERldmljZShvcHRpb25zKSkucGlwZShcbiAgICAgIG1lcmdlTWFwKGRldmljZSA9PiB7XG4gICAgICAgIHRoaXMuZGV2aWNlID0gZGV2aWNlO1xuICAgICAgICB0aGlzLl9kZXZpY2UkLmVtaXQoZGV2aWNlKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29uZmlndXJlRGV2aWNlRGlzY29ubmVjdGlvbiQoZGV2aWNlKS5waXBlKG1hcFRvKGRldmljZSkpO1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBjb25maWd1cmVEZXZpY2VEaXNjb25uZWN0aW9uJChkZXZpY2UpIHtcbiAgICByZXR1cm4gT2JzZXJ2YWJsZS5jcmVhdGUob2JzZXJ2ZXIgPT4ge1xuICAgICAgb2YoZGV2aWNlKVxuICAgICAgICAucGlwZShcbiAgICAgICAgICBtZXJnZU1hcChkZXYgPT4gZnJvbUV2ZW50KGRldmljZSwgJ2dhdHRzZXJ2ZXJkaXNjb25uZWN0ZWQnKSksXG4gICAgICAgICAgdGFrZSgxKVxuICAgICAgICApXG4gICAgICAgIC5zdWJzY3JpYmUoXG4gICAgICAgICAgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5fY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICdTZSBkZXNjb25lY3RhIGRpc3AgZW4gT25EZXZpY2UgZGlzY29ubmVjdGVkISEhISEhISdcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICB0aGlzLmRldmljZSA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLl9kZXZpY2UkLmVtaXQobnVsbCk7XG4gICAgICAgICAgfSxcbiAgICAgICAgICBlcnIgPT4ge1xuICAgICAgICAgICAgdGhpcy5fY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIEVycm9yIGluIG5vdGlmaWVyOiAnLCBlcnIpO1xuICAgICAgICAgICAgb2JzZXJ2ZXIuZXJyb3IoZXJyKTtcbiAgICAgICAgICB9LFxuICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgfVxuICAgICAgICApO1xuICAgICAgb2JzZXJ2ZXIubmV4dChgRGlzY29ubmVjdGlvbkV2ZW50IGFzIGJlZW4gcmVnaXN0ZXJgKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNjb3ZlciBhbGwgYXZhaWxhYmxlIGRldmljZXMgYW5kIGNvbm5lY3QgdG8gYSBzZWxlY3RlZCBkZXZpY2VcbiAgICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyB0byByZXF1ZXN0IHRoZSBkZXZpY2VzIHRoZSBzdHJ1Y3R1cmUgaXM6XG4gICAqIGFjY2VwdEFsbERldmljZXM6IHRydWV8ZmFsc2VcbiAgICogZmlsdGVyczogQmx1ZXRvb3RoRGF0YUZpbHRlckluaXQgKHNlZSBodHRwczovL3dlYmJsdWV0b290aGNnLmdpdGh1Yi5pby93ZWItYmx1ZXRvb3RoLyNkaWN0ZGVmLWJsdWV0b290aGxlc2NhbmZpbHRlcmluaXQgZm9yIG1vcmUgaW5mbylcbiAgICogb3B0aW9uYWxTZXJ2aWNlczogW10gKHNlcnZpY2VzIHRoYXQgYXJlIGdvaW5nIHRvIGJlIHVzZWQgaW5cbiAgICogY29tbXVuaWNhdGlvbiB3aXRoIHRoZSBkZXZpY2UsIG11c3QgdXNlIHRoZSBVSUlEIG9yIEdBVFQgaWRlbnRmaWVyIHRvIGxpc3QgdGhlciBzZXJ2aWNlcylcbiAgICogQHJldHVybnMgdGhlIGNvbm5lY3RlZCBkZXZpY2VcbiAgICovXG4gIGNvbm5lY3REZXZpY2UkKG9wdGlvbnM/OiBSZXF1ZXN0RGV2aWNlT3B0aW9ucykge1xuICAgIGlmICghb3B0aW9ucykge1xuICAgICAgb3B0aW9ucyA9IHtcbiAgICAgICAgYWNjZXB0QWxsRGV2aWNlczogdHJ1ZSxcbiAgICAgICAgb3B0aW9uYWxTZXJ2aWNlczogW11cbiAgICAgIH07XG4gICAgfSBlbHNlIGlmICghb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzKSB7XG4gICAgICBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMgPSBbXTtcbiAgICB9XG4gICAgb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzLnB1c2goR2F0dFNlcnZpY2VzLkdFTkVSSUNfQUNDRVNTLlNFUlZJQ0UpO1xuICAgIG9wdGlvbnMub3B0aW9uYWxTZXJ2aWNlcy5wdXNoKEdhdHRTZXJ2aWNlcy5CQVRURVJZLlNFUlZJQ0UpO1xuICAgIG9wdGlvbnMub3B0aW9uYWxTZXJ2aWNlcy5wdXNoKEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSk7XG4gICAgcmV0dXJuIHRoaXMuZGlzY292ZXJEZXZpY2UkKG9wdGlvbnMpLnBpcGUoXG4gICAgICBtZXJnZU1hcChkZXZpY2UgPT4ge1xuICAgICAgICByZXR1cm4gZGVmZXIoKCkgPT4gKGRldmljZSBhcyBhbnkpLmdhdHQuY29ubmVjdCgpKTtcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogRGlzY29ubmVjdCB0aGUgY3VycmVudCBkZXZpY2VcbiAgICovXG4gIGRpc2Nvbm5lY3REZXZpY2UoKSB7XG4gICAgaWYgKHRoaXMuZGV2aWNlKSB7XG4gICAgICB0aGlzLl9jb25zb2xlLmxvZygnc2UgZGVjb25lY3RhIGRpc3Bvc2l0aXZvJyk7XG4gICAgICB0aGlzLmRldmljZS5nYXR0LmRpc2Nvbm5lY3QoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogZ2V0IGEgZGF0YSBmcm9tIHRoZSBkZXZpY2UgdXNpbmcgdGhlIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFVVSUQgb3IgR0FUVCBpZGVudGlmaWVyIHNlcnZpY2VcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFVVSUQgb3IgR0FUVCBpZGVudGlmaWVyIGNoYXJhY3RlcmlzdGljXG4gICAqIEByZXR1cm4gVGhlIGNoYXJhY3RlcmlzdGljIGRhdGEgaW4gYSBEYXRhVmlldyBvYmplY3RcbiAgICovXG4gIHJlYWREZXZpY2VWYWx1ZSQoc2VydmljZSwgY2hhcmFjdGVyaXN0aWMpIHtcbiAgICBpZiAoIXRoaXMuZGV2aWNlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdNdXN0IHN0YXJ0IGEgY29ubmVjdGlvbiB0byBhIGRldmljZSBiZWZvcmUgcmVhZCB0aGUgZGV2aWNlIHZhbHVlJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHNlcnZpY2UpLnBpcGUoXG4gICAgICBtZXJnZU1hcChwcmltYXJ5U2VydmljZSA9PlxuICAgICAgICB0aGlzLmdldENoYXJhY3RlcmlzdGljJChwcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgICApLFxuICAgICAgbWVyZ2VNYXAoKGNoYXJhY3RlcmlzdGljVmFsdWU6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykgPT5cbiAgICAgICAgdGhpcy5yZWFkVmFsdWUkKGNoYXJhY3RlcmlzdGljVmFsdWUpXG4gICAgICApXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiB3cml0ZSBhIHZhbHVlIGluIHRoZSBzZWxlY3RlZCBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgdGhlIGNoYXJhY3RlcmlzaXRjIHdoZXJlIHlvdSB3YW50IHdyaXRlIHRoZSB2YWx1ZVxuICAgKiBAcGFyYW0gdmFsdWUgdmFsdWUgdGhlIHZhbHVlIHRvIHdyaXRlXG4gICAqL1xuICB3cml0ZURldmljZVZhbHVlJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgdmFsdWUpIHtcbiAgICBpZiAoIXRoaXMuZGV2aWNlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdNdXN0IHN0YXJ0IGEgY29ubmVjdGlvbiB0byBhIGRldmljZSBiZWZvcmUgcmVhZCB0aGUgZGV2aWNlIHZhbHVlJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHNlcnZpY2UpLnBpcGUoXG4gICAgICBtZXJnZU1hcChwcmltYXJ5U2VydmljZSA9PlxuICAgICAgICB0aGlzLmdldENoYXJhY3RlcmlzdGljJChwcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgICApLFxuICAgICAgbWVyZ2VNYXAoKGNoYXJhY3RlcmlzdGljVmFsdWU6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykgPT5cbiAgICAgICAgdGhpcy53cml0ZVZhbHVlJChjaGFyYWN0ZXJpc3RpY1ZhbHVlLCB2YWx1ZSlcbiAgICAgIClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIGdldCBhIHByaW1hcnkgc2VydmljZSBpbnN0YW5jZSB1c2luZyB0aGUgc2VydmljZSBVSUlEIG9yIEdBVFQgaWRlbnRpZmllclxuICAgKiBAcGFyYW0gc2VydmljZSBzZXJ2aWNlIGlkZW50aWZpZXJcbiAgICogQHJldHVybiBzZXJ2aWNlIGluc3RhbmNlXG4gICAqL1xuICBnZXRQcmltYXJ5U2VydmljZSQoc2VydmljZTogQmx1ZXRvb3RoU2VydmljZVVVSUQpIHtcbiAgICByZXR1cm4gb2YodGhpcy5kZXZpY2UpLnBpcGUoXG4gICAgICBtZXJnZU1hcChkZXZpY2UgPT4ge1xuICAgICAgICByZXR1cm4gZGV2aWNlLmdhdHQuZ2V0UHJpbWFyeVNlcnZpY2Uoc2VydmljZSk7XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgY2hhcmFjdGVyaXNpdGljIGluc3RhbmNlIHVzaW5nIHRoZSBzZXJ2aWNlIGluc3RhbmNlIGFuZCBhIGNoYXJhY3RlcmlzdGljIFVVSURcbiAgICogQHBhcmFtIHByaW1hcnlTZXJ2aWNlIHNlcnZpY2UgaW5zdGFuY2VcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIGNoYXJhY3RlcmlzaXRpYyBpZGVudGlmaWVyXG4gICAqIEByZXR1cm4gY2hhcmFjdGVyaXN0aWMgaW5zdGFuY2VcbiAgICovXG4gIGdldENoYXJhY3RlcmlzdGljJChcbiAgICBwcmltYXJ5U2VydmljZTogQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZpY2UsXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRFxuICApOiBPYnNlcnZhYmxlPHZvaWQgfCBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWM+IHtcbiAgICByZXR1cm4gZGVmZXIoKCkgPT4gcHJpbWFyeVNlcnZpY2UuZ2V0Q2hhcmFjdGVyaXN0aWMoY2hhcmFjdGVyaXN0aWMpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiByZWFkIHRoZSBjaGFyYWN0ZXJpc3RpYyB2YWx1ZVxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgY2hhcmFjdGVyaXN0aWMgaW5zdGFuY2VcbiAgICogQHJldHVybiBUaGUgY2hhcmFjdGVyaXN0aWMgZGF0YSBpbiBhIERhdGFWaWV3IG9iamVjdFxuICAgKi9cbiAgcHJpdmF0ZSByZWFkVmFsdWUkKFxuICAgIGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWNcbiAgKTogT2JzZXJ2YWJsZTxEYXRhVmlldz4ge1xuICAgIHJldHVybiBmcm9tKFxuICAgICAgY2hhcmFjdGVyaXN0aWNcbiAgICAgICAgLnJlYWRWYWx1ZSgpXG4gICAgICAgIC50aGVuKFxuICAgICAgICAgIChkYXRhOiBEYXRhVmlldykgPT4gUHJvbWlzZS5yZXNvbHZlKGRhdGEpLFxuICAgICAgICAgIChlcnJvcjogRE9NRXhjZXB0aW9uKSA9PiBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfWApXG4gICAgICAgIClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIHdyaXRlIGEgdmFsdWUgaW4gdGhlIHNlbGVjdGVkIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyB0aGUgY2hhcmFjdGVyaXNpdGMgd2hlcmUgeW91IHdhbnQgd3JpdGUgdGhlIHZhbHVlXG4gICAqIEBwYXJhbSB2YWx1ZSB0aGUgdmFsdWUgdG8gd3JpdGVcbiAgICovXG4gIHByaXZhdGUgd3JpdGVWYWx1ZSQoXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYyxcbiAgICB2YWx1ZTogQXJyYXlCdWZmZXIgfCBVaW50OEFycmF5XG4gICkge1xuICAgIHJldHVybiBkZWZlcigoKSA9PlxuICAgICAgY2hhcmFjdGVyaXN0aWNcbiAgICAgICAgLndyaXRlVmFsdWUodmFsdWUpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBjaGFuZ2UgdGhlIHN0YXRlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpYyB0byBlbmFibGUgaXRcbiAgICogQHBhcmFtIHNlcnZpY2UgIHBhcmVudCBzZXJ2aWNlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgY2hhcmFjdGVyaXN0aWMgdG8gY2hhbmdlIHRoZSBzdGF0ZVxuICAgKiBAcGFyYW0gc3RhdGUgbmV3IHN0YXRlXG4gICAqL1xuICBlbmFibGVDaGFyYWN0ZXJpc3RpYyQoXG4gICAgc2VydmljZTogQmx1ZXRvb3RoU2VydmljZVVVSUQsXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRCxcbiAgICBzdGF0ZT86IGFueVxuICApIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IG5ldyBVaW50OEFycmF5KFsxXSk7XG4gICAgcmV0dXJuIHRoaXMuc2V0Q2hhcmFjdGVyaXN0aWNTdGF0ZSQoc2VydmljZSwgY2hhcmFjdGVyaXN0aWMsIHN0YXRlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBjaGFuZ2UgdGhlIHN0YXRlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpYyB0byBkaXNhYmxlIGl0XG4gICAqIEBwYXJhbSBzZXJ2aWNlICBwYXJlbnQgc2VydmljZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWNcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIGNoYXJhY3RlcmlzdGljIHRvIGNoYW5nZSB0aGUgc3RhdGVcbiAgICogQHBhcmFtIHN0YXRlIG5ldyBzdGF0ZVxuICAgKi9cbiAgZGlzYmFsZUNoYXJhY3RlcmlzdGljJChcbiAgICBzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCxcbiAgICBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoQ2hhcmFjdGVyaXN0aWNVVUlELFxuICAgIHN0YXRlPzogYW55XG4gICkge1xuICAgIHN0YXRlID0gc3RhdGUgfHwgbmV3IFVpbnQ4QXJyYXkoWzBdKTtcbiAgICByZXR1cm4gdGhpcy5zZXRDaGFyYWN0ZXJpc3RpY1N0YXRlJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgc3RhdGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIHNldCBhIHN0YXRlIHRvIGFuIHNwZWNpZmljIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBzZXJ2aWNlICBwYXJlbnQgc2VydmljZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWNcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIGNoYXJhY3RlcmlzdGljIHRvIGNoYW5nZSB0aGUgc3RhdGVcbiAgICogQHBhcmFtIHN0YXRlIG5ldyBzdGF0ZVxuICAgKiBAcmV0dXJuXG4gICAqL1xuICBzZXRDaGFyYWN0ZXJpc3RpY1N0YXRlJChcbiAgICBzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCxcbiAgICBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoQ2hhcmFjdGVyaXN0aWNVVUlELFxuICAgIHN0YXRlOiBBcnJheUJ1ZmZlclxuICApIHtcbiAgICBjb25zdCBwcmltYXJ5U2VydmljZSA9IHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHNlcnZpY2UpO1xuICAgIHJldHVybiBwcmltYXJ5U2VydmljZS5waXBlKFxuICAgICAgbWVyZ2VNYXAoX3ByaW1hcnlTZXJ2aWNlID0+XG4gICAgICAgIHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWMkKF9wcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgICApLFxuICAgICAgbWFwKChfY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykgPT5cbiAgICAgICAgdGhpcy53cml0ZVZhbHVlJChfY2hhcmFjdGVyaXN0aWMsIHN0YXRlKVxuICAgICAgKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogU2VuZCBhIG1lc3NhZ2UgdXNpbmcgYSBub3RpZmllciBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gbWVzc2FnZSBtZXNzYWdlIHRvIHNlbmRcbiAgICogQHBhcmFtIHNlcnZpY2Ugc2VydmljZSB0byB3aGljaCB0aGUgY2hhcmFjdGVyaXN0aWMgYmVsb25nc1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgZmVhdHVyZSBpbiB3aGljaCB5b3Ugd2FudCB0byBzZW5kIHRoZSBub3RpZmljYXRpb25cbiAgICovXG4gIHNlbmRUb05vdGlmaWVyJChtZXNzYWdlLCBzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYykge1xuICAgIHJldHVybiB0aGlzLmdldFByaW1hcnlTZXJ2aWNlJChzZXJ2aWNlKS5waXBlKFxuICAgICAgbWVyZ2VNYXAoKHByaW1hcnlTZXJ2aWNlOiBCbHVldG9vdGhSZW1vdGVHQVRUU2VydmljZSkgPT5cbiAgICAgICAgdGhpcy5nZXRDaGFyYWN0ZXJpc3RpYyQocHJpbWFyeVNlcnZpY2UsIGNoYXJhY3RlcmlzdGljKVxuICAgICAgKSxcbiAgICAgIG1lcmdlTWFwKChjaGFyOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMpID0+IHtcbiAgICAgICAgaWYgKG1lc3NhZ2UubGVuZ3RoID4gMTYpIHtcbiAgICAgICAgICBsZXQgb2JzVGVzdCA9IG9mKHVuZGVmaW5lZCk7XG4gICAgICAgICAgd2hpbGUgKG1lc3NhZ2UubGVuZ3RoID4gMTYpIHtcbiAgICAgICAgICAgIG9ic1Rlc3QgPSBvYnNUZXN0LnBpcGUoXG4gICAgICAgICAgICAgIGNvbmNhdCh0aGlzLndyaXRlVmFsdWUkKGNoYXIsIG1lc3NhZ2Uuc2xpY2UoMCwgMTYpKSlcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBtZXNzYWdlID0gbWVzc2FnZS5zbGljZSgxNiwgbWVzc2FnZS5sZW5ndGgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAobWVzc2FnZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBvYnNUZXN0ID0gb2JzVGVzdC5waXBlKGNvbmNhdCh0aGlzLndyaXRlVmFsdWUkKGNoYXIsIG1lc3NhZ2UpKSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBvYnNUZXN0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiB0aGlzLndyaXRlVmFsdWUkKGNoYXIsIG1lc3NhZ2UpO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFRoZSBCYXR0ZXJ5IExldmVsIGNoYXJhY3RlcmlzdGljIGlzIHJlYWQgdXNpbmcgdGhlIEdBVFQgUmVhZCBDaGFyYWN0ZXJpc3RpY1xuICAgKiAgVmFsdWUgc3ViLXByb2NlZHVyZSBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBiYXR0ZXJ5IGxldmVsIGFzIGEgcGVyY2VudGFnZVxuICAgKiAgZnJvbSAwJSB0byAxMDAlOyAwJSByZXByZXNlbnRzIGEgYmF0dGVyeSB0aGF0IGlzIGZ1bGx5IGRpc2NoYXJnZWQsIDEwMCVcbiAgICogcmVwcmVzZW50cyBhIGJhdHRlcnkgdGhhdCBpcyBmdWxseSBjaGFyZ2VkXG4gICAqL1xuICBnZXRCYXR0ZXJ5TGV2ZWwkKCkge1xuICAgIHJldHVybiB0aGlzLnJlYWREZXZpY2VWYWx1ZSQoXG4gICAgICBHYXR0U2VydmljZXMuQkFUVEVSWS5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkJBVFRFUlkuQkFUVEVSWV9MRVZFTFxuICAgICkucGlwZShtYXAodmFsdWUgPT4gdmFsdWUuZ2V0VWludDgoMCkpKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBuYW1lIG9mIHRoZSBtYW51ZmFjdHVyZXIgb2YgdGhlIGRldmljZS5cbiAgICovXG4gIGdldE1hbnVmYWN0dXJlck5hbWUkKCkge1xuICAgIHJldHVybiB0aGlzLnJlYWREZXZpY2VWYWx1ZSQoXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLlNFUlZJQ0UsXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLk1BTlVGQUNUVVJFUl9OQU1FXG4gICAgKS5waXBlKFxuICAgICAgbWFwKGRhdGFWaWV3ID0+XG4gICAgICAgIHRoaXMuY3lwaGVyQWVzU2VydmljZS5ieXRlc1RvVGV4dChuZXcgVWludDhBcnJheShkYXRhVmlldy5idWZmZXIpKVxuICAgICAgKVxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFRoaXMgY2hhcmFjdGVyaXN0aWMgcmVwcmVzZW50cyB0aGUgbW9kZWwgbnVtYmVyIHRoYXQgaXMgYXNzaWduZWQgYnkgdGhlIGRldmljZSB2ZW5kb3IuXG4gICAqL1xuICBnZXRNb2RlbE51bWJlciQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uTU9ERUxfTlVNQkVSXG4gICAgKS5waXBlKFxuICAgICAgbWFwKGRhdGFWaWV3ID0+XG4gICAgICAgIHRoaXMuY3lwaGVyQWVzU2VydmljZS5ieXRlc1RvVGV4dChuZXcgVWludDhBcnJheShkYXRhVmlldy5idWZmZXIpKVxuICAgICAgKVxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFRoaXMgY2hhcmFjdGVyaXN0aWMgcmVwcmVzZW50cyB0aGUgc2VyaWFsIG51bWJlciBmb3IgYSBwYXJ0aWN1bGFyIGluc3RhbmNlIG9mIHRoZSBkZXZpY2UuXG4gICAqL1xuICBnZXRTZXJpYWxOdW1iZXIkKCkge1xuICAgIHJldHVybiB0aGlzLnJlYWREZXZpY2VWYWx1ZSQoXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLlNFUlZJQ0UsXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLlNFUklBTF9OVU1CRVJcbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBoYXJkd2FyZSByZXZpc2lvbiBmb3IgdGhlIGhhcmR3YXJlIHdpdGhpbiB0aGUgZGV2aWNlLlxuICAgKi9cbiAgZ2V0SGFyZHdhcmVSZXZpc2lvbiQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uSEFSRFdBUkVfUkVWSVNJT05cbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBmaXJtd2FyZSByZXZpc2lvbiBmb3IgdGhlIGZpcm13YXJlIHdpdGhpbiB0aGUgZGV2aWNlLlxuICAgKi9cbiAgZ2V0RmlybXdhcmVSZXZpc2lvbiQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uRklSTVdBUkVfUkVWSVNJT05cbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBzb2Z0d2FyZSByZXZpc2lvbiBmb3IgdGhlIHNvZnR3YXJlIHdpdGhpbiB0aGUgZGV2aWNlLlxuICAgKi9cbiAgZ2V0U29mdHdhcmVSZXZpc2lvbiQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU09GVFdBUkVfUkVWSVNJT05cbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIGEgc3RydWN0dXJlIGNvbnRhaW5pbmcgYW4gT3JnYW5pemF0aW9uYWxseSBVbmlxdWUgSWRlbnRpZmllclxuICAgKiAgKE9VSSkgZm9sbG93ZWQgYnkgYSBtYW51ZmFjdHVyZXItZGVmaW5lZCBpZGVudGlmaWVyIGFuZCBpcyB1bmlxdWUgZm9yIGVhY2ggaW5kaXZpZHVhbCBpbnN0YW5jZSBvZiB0aGUgcHJvZHVjdC5cbiAgICovXG4gIGdldFN5c3RlbUlkJCgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkRGV2aWNlVmFsdWUkKFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TWVNURU1fSURcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBUaGUgUG5QX0lEIGNoYXJhY3RlcmlzdGljIGlzIGEgc2V0IG9mIHZhbHVlcyB1c2VkIHRvIGNyZWF0ZSBhIGRldmljZSBJRCB2YWx1ZSB0aGF0IGlzIHVuaXF1ZSBmb3IgdGhpcyBkZXZpY2UuXG4gICAqL1xuICBnZXRQbnBJZCQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uUE5QX0lEXG4gICAgKTtcbiAgfVxufVxuIl19