dragon: Add DragonKeyboardFirmwareUpdater service.

DragonKeyboardFirmwareUpdater is a built-in system service constantly running
in background and detecting if the connected Pixel C keyboard needs to be
updated. This service imports dfu library as a dependency to perform keyboard
firmware update. Except for the hacks mentioned in dfu/README.google, the
remaining codes are original.

This is the initial commit of DragonKeyboardFirmwareUpdater. The below
issues need to be resolved in the following CLs:
(1) android.permission.ACCESS_COARSE_LOCATION should be granted without user
interaction.
(2) The UI needs to be polished according to approved UI designed.
(3) Adding Pixel C Keyboard entry in System Settings to provide an entry for
postponed update.
(4) Remove hacks in dfu library.

Bug:25369339
Change-Id: I28b9b8ae671f2fb4a417fc45a9709aa6858f5c3e
diff --git a/DragonKeyboardFirmwareUpdater/Android.mk b/DragonKeyboardFirmwareUpdater/Android.mk
new file mode 100644
index 0000000..0263db8
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/Android.mk
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(call all-subdir-makefiles)
diff --git a/DragonKeyboardFirmwareUpdater/README.google b/DragonKeyboardFirmwareUpdater/README.google
new file mode 100644
index 0000000..c07a031
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/README.google
@@ -0,0 +1,5 @@
+DragonKeyboardFirmwareUpdater is a firmware OTA update service serving the
+combination of Pixel C and Pixel C Keyboard. DragonKeyboardFrimwareUpdater makes
+use of Android-DFU-Library, which is an open source project initiated by Nordic
+Semiconductor Co.. The license is included in dfu folder. See
+https://github.com/NordicSemiconductor/Android-DFU-Library for more information.
diff --git a/DragonKeyboardFirmwareUpdater/dfu/.gitignore b/DragonKeyboardFirmwareUpdater/dfu/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/DragonKeyboardFirmwareUpdater/dfu/Android.mk b/DragonKeyboardFirmwareUpdater/dfu/Android.mk
new file mode 100644
index 0000000..b6637ff
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dfu-library
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := 22
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 libgson
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := libgson:gson-2.3.1.jar
+
+include $(BUILD_MULTI_PREBUILT)
diff --git a/DragonKeyboardFirmwareUpdater/dfu/AndroidManifest.xml b/DragonKeyboardFirmwareUpdater/dfu/AndroidManifest.xml
new file mode 100644
index 0000000..e1ad77c
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+	package="no.nordicsemi.android.dfu">
+
+	<uses-permission android:name="android.permission.BLUETOOTH"/>
+	<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+</manifest>
diff --git a/DragonKeyboardFirmwareUpdater/dfu/LICENSE b/DragonKeyboardFirmwareUpdater/dfu/LICENSE
new file mode 100644
index 0000000..cf4aba8
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2015, Nordic Semiconductor
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of Android-DFU-Library nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/DragonKeyboardFirmwareUpdater/dfu/README.google b/DragonKeyboardFirmwareUpdater/dfu/README.google
new file mode 100644
index 0000000..c26b842
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/README.google
@@ -0,0 +1,3 @@
+HACKs in src/no/nordicsemi/android/dfu/DfuBaseService.java:
+mPackageBeforeNotification = 2
+BuildConfig.DEBUG
diff --git a/DragonKeyboardFirmwareUpdater/dfu/gson-2.3.1.jar b/DragonKeyboardFirmwareUpdater/dfu/gson-2.3.1.jar
new file mode 100644
index 0000000..250132c
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/gson-2.3.1.jar
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/dfu/res/drawable-hdpi/ic_action_notify_cancel.png b/DragonKeyboardFirmwareUpdater/dfu/res/drawable-hdpi/ic_action_notify_cancel.png
new file mode 100644
index 0000000..e206f29
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/res/drawable-hdpi/ic_action_notify_cancel.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/dfu/res/drawable-hdpi/ic_stat_notify_dfu.png b/DragonKeyboardFirmwareUpdater/dfu/res/drawable-hdpi/ic_stat_notify_dfu.png
new file mode 100644
index 0000000..261078d
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/res/drawable-hdpi/ic_stat_notify_dfu.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/dfu/res/drawable-xhdpi/ic_action_notify_cancel.png b/DragonKeyboardFirmwareUpdater/dfu/res/drawable-xhdpi/ic_action_notify_cancel.png
new file mode 100644
index 0000000..d163420
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/res/drawable-xhdpi/ic_action_notify_cancel.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/dfu/res/drawable-xhdpi/ic_stat_notify_dfu.png b/DragonKeyboardFirmwareUpdater/dfu/res/drawable-xhdpi/ic_stat_notify_dfu.png
new file mode 100644
index 0000000..6009fd1
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/res/drawable-xhdpi/ic_stat_notify_dfu.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/dfu/res/values/strings.xml b/DragonKeyboardFirmwareUpdater/dfu/res/values/strings.xml
new file mode 100644
index 0000000..89c9fa7
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/res/values/strings.xml
@@ -0,0 +1,31 @@
+<resources>
+
+    <string name="dfu_unknown_name">unnamed device</string>
+    
+    <string name="dfu_action_abort">Abort</string>
+
+    <string name="dfu_status_initializing">Initializing&#8230;</string>
+    <string name="dfu_status_connecting">Connecting&#8230;</string>
+    <string name="dfu_status_starting">Starting DFU&#8230;</string>
+    <string name="dfu_status_switching_to_dfu">Starting bootloader&#8230;</string>
+    <string name="dfu_status_uploading">Uploading&#8230;</string>
+    <string name="dfu_status_uploading_part" formatted="false">Uploading part %d/%d&#8230;</string>
+    <string name="dfu_status_validating">Validating&#8230;</string>
+    <string name="dfu_status_disconnecting">Disconnecting&#8230;</string>
+    <string name="dfu_status_completed">Done</string>
+    <string name="dfu_status_aborted">Aborted</string>
+    <string name="dfu_status_aborting">Aborting&#8230;</string>
+    <string name="dfu_status_error">Upload failed</string>
+    
+    <string name="dfu_status_connecting_msg">Connecting to %s&#8230;</string>
+    <string name="dfu_status_starting_msg">Initializing DFU process&#8230;</string>
+    <string name="dfu_status_switching_to_dfu_msg">Waiting for bootloader&#8230;</string>
+    <string name="dfu_status_uploading_components_msg">Transmitting components to %s&#8230;</string>
+    <string name="dfu_status_uploading_msg">Transmitting application to %s&#8230;</string>
+    <string name="dfu_status_validating_msg">Validating&#8230;</string>
+    <string name="dfu_status_completed_msg">Application has been send successfully.</string>
+    <string name="dfu_status_aborted_msg">Application upload has been canceled.</string>
+    <string name="dfu_status_disconnecting_msg">Disconnecting from %s&#8230;</string>
+    <string name="dfu_status_error_msg">DFU process failed.</string>
+    
+</resources>
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuBaseService.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuBaseService.java
new file mode 100644
index 0000000..5d6a3e1
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuBaseService.java
@@ -0,0 +1,2938 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.IntentService;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Build;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.Locale;
+import java.util.UUID;
+
+import no.nordicsemi.android.dfu.internal.ArchiveInputStream;
+import no.nordicsemi.android.dfu.internal.HexInputStream;
+import no.nordicsemi.android.dfu.internal.exception.DeviceDisconnectedException;
+import no.nordicsemi.android.dfu.internal.exception.DfuException;
+import no.nordicsemi.android.dfu.internal.exception.HexFileValidationException;
+import no.nordicsemi.android.dfu.internal.exception.RemoteDfuException;
+import no.nordicsemi.android.dfu.internal.exception.SizeValidationException;
+import no.nordicsemi.android.dfu.internal.exception.UnknownResponseException;
+import no.nordicsemi.android.dfu.internal.exception.UploadAbortedException;
+import no.nordicsemi.android.dfu.internal.scanner.BootloaderScannerFactory;
+import no.nordicsemi.android.error.GattError;
+
+/**
+ * The DFU Service provides full support for Over-the-Air (OTA) Device Firmware Update (DFU) by Nordic Semiconductor.
+ * With the Soft Device 7.0.0+ it allows to upload a new Soft Device, new Bootloader and a new Application. For older soft devices only the Application update is supported.
+ * <p>
+ * To run the service to your application extend it in your project and overwrite the missing method. Remember to add your class to the AndroidManifest.xml file.
+ * </p>
+ * <p>
+ * The {@link DfuServiceInitiator} object should be used to start the DFU Service.
+ * <p/>
+ * <pre>
+ * final DfuServiceInitiator starter = new DfuServiceInitiator(mSelectedDevice.getAddress())
+ * 		.setDeviceName(mSelectedDevice.getName())
+ * 		.setKeepBond(keepBond)
+ * 		.setZip(mFileStreamUri, mFilePath)
+ *		.start(this, DfuService.class);
+ * </pre>
+ * <p>
+ *     You may register the progress and log listeners using the {@link DfuServiceListenerHelper} helper class. See {@link DfuProgressListener} and {@link DfuLogListener} for
+ *     more information.
+ * </p>
+ * <p>
+ * The service will show its progress on the notification bar and will send local broadcasts to the application.
+ * </p>
+ */
+public abstract class DfuBaseService extends IntentService {
+	private static final String TAG = "DfuBaseService";
+
+	/**
+	 * The address of the device to update.
+	 */
+	public static final String EXTRA_DEVICE_ADDRESS = "no.nordicsemi.android.dfu.extra.EXTRA_DEVICE_ADDRESS";
+	/**
+	 * The optional device name. This name will be shown in the notification.
+	 */
+	public static final String EXTRA_DEVICE_NAME = "no.nordicsemi.android.dfu.extra.EXTRA_DEVICE_NAME";
+	/**
+	 * A boolean indicating whether to disable the progress notification in the status bar. Defaults to false.
+	 */
+	public static final String EXTRA_DISABLE_NOTIFICATION = "no.nordicsemi.android.dfu.extra.EXTRA_DISABLE_NOTIFICATION";
+	/**
+	 * <p>
+	 * If the new firmware (application) does not share the bond information with the old one, the bond information is lost. Set this flag to <code>true</code>
+	 * to make the service create new bond with the new application when the upload is done (and remove the old one). When set to <code>false</code> (default),
+	 * the DFU service assumes that the LTK is shared between them. Note: currently it is not possible to remove the old bond without creating a new one so if
+	 * your old application supported bonding while the new one does not you have to modify the source code yourself.
+	 * </p>
+	 * <p>
+	 * In case of updating the soft device the application is always removed together with the bond information.
+	 * </p>
+	 * <p>
+	 * Search for occurrences of EXTRA_RESTORE_BOND in this file to check the implementation and get more details.
+	 * </p>
+	 */
+	public static final String EXTRA_RESTORE_BOND = "no.nordicsemi.android.dfu.extra.EXTRA_RESTORE_BOND";
+	/**
+	 * <p>This flag indicated whether the bond information should be kept or removed after an upgrade of the Application.
+	 * If an application is being updated on a bonded device with the DFU Bootloader that has been configured to preserve the bond information for the new application,
+	 * set it to <code>true</code>.</p>
+	 *
+	 * <p>By default the DFU Bootloader clears the whole application's memory. It may be however configured in the \Nordic\nrf51\components\libraries\bootloader_dfu\dfu_types.h
+	 * file (line 56: <code>#define DFU_APP_DATA_RESERVED 0x0000</code>) to preserve some pages. The BLE_APP_HRM_DFU sample app stores the LTK and System Attributes in the first
+	 * two pages, so in order to preserve the bond information this value should be changed to 0x0800 or more.
+	 * When those data are preserved, the new Application will notify the app with the Service Changed indication when launched for the first time. Otherwise this
+	 * service will remove the bond information from the phone and force to refresh the device cache (see {@link #refreshDeviceCache(android.bluetooth.BluetoothGatt, boolean)}).</p>
+	 *
+	 * <p>In contrast to {@link #EXTRA_RESTORE_BOND} this flag will not remove the old bonding and recreate a new one, but will keep the bond information untouched.</p>
+	 * <p>The default value of this flag is <code>false</code></p>
+	 */
+	public static final String EXTRA_KEEP_BOND = "no.nordicsemi.android.dfu.extra.EXTRA_KEEP_BOND";
+	/**
+	 * A path to the file with the new firmware. It may point to a HEX, BIN or a ZIP file.
+	 * Some file manager applications return the path as a String while other return a Uri. Use the {@link #EXTRA_FILE_URI} in the later case.
+	 * For files included in /res/raw resource directory please use {@link #EXTRA_FILE_RES_ID} instead.
+	 */
+	public static final String EXTRA_FILE_PATH = "no.nordicsemi.android.dfu.extra.EXTRA_FILE_PATH";
+	/**
+	 * See {@link #EXTRA_FILE_PATH} for details.
+	 */
+	public static final String EXTRA_FILE_URI = "no.nordicsemi.android.dfu.extra.EXTRA_FILE_URI";
+	/**
+	 * See {@link #EXTRA_FILE_PATH} for details.
+	 */
+	public static final String EXTRA_FILE_RES_ID = "no.nordicsemi.android.dfu.extra.EXTRA_FILE_RES_ID";
+	/**
+	 * The Init packet URI. This file is required if the Extended Init Packet is required (SDK 7.0+). Must point to a 'dat' file corresponding with the selected firmware.
+	 * The Init packet may contain just the CRC (in case of older versions of DFU) or the Extended Init Packet in binary format (SDK 7.0+).
+	 */
+	public static final String EXTRA_INIT_FILE_PATH = "no.nordicsemi.android.dfu.extra.EXTRA_INIT_FILE_PATH";
+	/**
+	 * The Init packet URI. This file is required if the Extended Init Packet is required (SDK 7.0+). Must point to a 'dat' file corresponding with the selected firmware.
+	 * The Init packet may contain just the CRC (in case of older versions of DFU) or the Extended Init Packet in binary format (SDK 7.0+).
+	 */
+	public static final String EXTRA_INIT_FILE_URI = "no.nordicsemi.android.dfu.extra.EXTRA_INIT_FILE_URI";
+	/**
+	 * The Init packet URI. This file is required if the Extended Init Packet is required (SDK 7.0+). Must point to a 'dat' file corresponding with the selected firmware.
+	 * The Init packet may contain just the CRC (in case of older versions of DFU) or the Extended Init Packet in binary format (SDK 7.0+).
+	 */
+	public static final String EXTRA_INIT_FILE_RES_ID = "no.nordicsemi.android.dfu.extra.EXTRA_INIT_FILE_RES_ID";
+	/**
+	 * The input file mime-type. Currently only "application/zip" (ZIP) or "application/octet-stream" (HEX or BIN) are supported. If this parameter is
+	 * empty the "application/octet-stream" is assumed.
+	 */
+	public static final String EXTRA_FILE_MIME_TYPE = "no.nordicsemi.android.dfu.extra.EXTRA_MIME_TYPE";
+	// Since the DFU Library version 0.5 both HEX and BIN files are supported. As both files have the same MIME TYPE the distinction is made based on the file extension.
+	public static final String MIME_TYPE_OCTET_STREAM = "application/octet-stream";
+	public static final String MIME_TYPE_ZIP = "application/zip";
+	/**
+	 * This optional extra parameter may contain a file type. Currently supported are:
+	 * <ul>
+	 * <li>{@link #TYPE_SOFT_DEVICE} - only Soft Device update</li>
+	 * <li>{@link #TYPE_BOOTLOADER} - only Bootloader update</li>
+	 * <li>{@link #TYPE_APPLICATION} - only application update</li>
+	 * <li>{@link #TYPE_AUTO} - the file is a ZIP file that may contain more than one HEX/BIN + DAT files. Since SDK 8.0 the ZIP Distribution packet is a recommended
+	 * way of delivering firmware files. Please, see the DFU documentation for more details. A ZIP distribution packet may be created using the 'nrf utility'
+	 * command line application, that is a part of Master Control Panel 3.8.0.The ZIP file MAY contain only the following files:
+	 * <b>softdevice.hex/bin</b>, <b>bootloader.hex/bin</b>, <b>application.hex/bin</b> to determine the type based on its name. At lease one of them MUST be present.
+	 * </li>
+	 * </ul>
+	 * If this parameter is not provided the type is assumed as follows:
+	 * <ol>
+	 * <li>If the {@link #EXTRA_FILE_MIME_TYPE} field is <code>null</code> or is equal to {@value #MIME_TYPE_OCTET_STREAM} - the {@link #TYPE_APPLICATION} is assumed.</li>
+	 * <li>If the {@link #EXTRA_FILE_MIME_TYPE} field is equal to {@value #MIME_TYPE_ZIP} - the {@link #TYPE_AUTO} is assumed.</li>
+	 * </ol>
+	 */
+	public static final String EXTRA_FILE_TYPE = "no.nordicsemi.android.dfu.extra.EXTRA_FILE_TYPE";
+	/**
+	 * <p>
+	 * The file contains a new version of Soft Device.
+	 * </p>
+	 * <p>
+	 * Since DFU Library 7.0 all firmware may contain an Init packet. The Init packet is required if Extended Init Packet is used by the DFU bootloader (SDK 7.0+)..
+	 * The Init packet for the bootloader must be placed in the .dat file.
+	 * </p>
+	 *
+	 * @see #EXTRA_FILE_TYPE
+	 */
+	public static final int TYPE_SOFT_DEVICE = 0x01;
+	/**
+	 * <p>
+	 * The file contains a new version of Bootloader.
+	 * </p>
+	 * <p>
+	 * Since DFU Library 7.0 all firmware may contain an Init packet. The Init packet is required if Extended Init Packet is used by the DFU bootloader (SDK 7.0+).
+	 * The Init packet for the bootloader must be placed in the .dat file.
+	 * </p>
+	 *
+	 * @see #EXTRA_FILE_TYPE
+	 */
+	public static final int TYPE_BOOTLOADER = 0x02;
+	/**
+	 * <p>
+	 * The file contains a new version of Application.
+	 * </p>
+	 * <p>
+	 * Since DFU Library 0.5 all firmware may contain an Init packet. The Init packet is required if Extended Init Packet is used by the DFU bootloader (SDK 7.0+).
+	 * The Init packet for the application must be placed in the .dat file.
+	 * </p>
+	 *
+	 * @see #EXTRA_FILE_TYPE
+	 */
+	public static final int TYPE_APPLICATION = 0x04;
+	/**
+	 * <p>
+	 * A ZIP file that consists of more than 1 file. Since SDK 8.0 the ZIP Distribution packet is a recommended way of delivering firmware files. Please, see the DFU documentation for
+	 * more details. A ZIP distribution packet may be created using the 'nrf utility' command line application, that is a part of Master Control Panel 3.8.0.
+	 * For backwards compatibility this library supports also ZIP files without the manifest file. Instead they must follow the fixed naming convention:
+	 * The names of files in the ZIP must be: <b>softdevice.hex</b> (or .bin), <b>bootloader.hex</b> (or .bin), <b>application.hex</b> (or .bin) in order
+	 * to be read correctly. Using the Soft Device v7.0.0+ the Soft Device and Bootloader may be updated and sent together. In case of additional application file included,
+	 * the service will try to send Soft Device, Bootloader and Application together (which is not supported currently) and if it fails, send first SD+BL, reconnect and send the application
+	 * in the following connection.
+	 * </p>
+	 * <p>
+	 * Since the DFU Library 0.5 you may specify the Init packet, that will be send prior to the firmware. The init packet contains some verification data, like a device type and
+	 * revision, application version or a list of supported Soft Devices. The Init packet is required if Extended Init Packet is used by the DFU bootloader (SDK 7.0+).
+	 * In case of using the compatibility ZIP files the Init packet for the Soft Device and Bootloader must be in the 'system.dat' file while for the application
+	 * in the 'application.dat' file (included in the ZIP). The CRC in the 'system.dat' must be a CRC of both BIN contents if both a Soft Device and a Bootloader is present.
+	 * </p>
+	 *
+	 * @see #EXTRA_FILE_TYPE
+	 */
+	public static final int TYPE_AUTO = 0x00;
+	/**
+	 * An extra field with progress and error information used in broadcast events.
+	 */
+	public static final String EXTRA_DATA = "no.nordicsemi.android.dfu.extra.EXTRA_DATA";
+	/**
+	 * An extra field to send the progress or error information in the DFU notification. The value may contain:
+	 * <ul>
+	 * <li>Value 0 - 100 - percentage progress value</li>
+	 * <li>One of the following status constants:
+	 * <ul>
+	 * <li>{@link #PROGRESS_CONNECTING}</li>
+	 * <li>{@link #PROGRESS_STARTING}</li>
+	 * <li>{@link #PROGRESS_ENABLING_DFU_MODE}</li>
+	 * <li>{@link #PROGRESS_VALIDATING}</li>
+	 * <li>{@link #PROGRESS_DISCONNECTING}</li>
+	 * <li>{@link #PROGRESS_COMPLETED}</li>
+	 * <li>{@link #PROGRESS_ABORTED}</li>
+	 * </ul>
+	 * </li>
+	 * <li>An error code with {@link #ERROR_MASK} if initialization error occurred</li>
+	 * <li>An error code with {@link #ERROR_REMOTE_MASK} if remote DFU target returned an error</li>
+	 * <li>An error code with {@link #ERROR_CONNECTION_MASK} if connection error occurred (f.e. GATT error (133) or Internal GATT Error (129))</li>
+	 * </ul>
+	 * To check if error occurred use:<br />
+	 * {@code boolean error = progressValue >= DfuBaseService.ERROR_MASK;}
+	 */
+	public static final String EXTRA_PROGRESS = "no.nordicsemi.android.dfu.extra.EXTRA_PROGRESS";
+	/**
+	 * The number of currently transferred part. The SoftDevice and Bootloader may be send together as one part. If user wants to upload them together with an application it has to be sent
+	 * in another connection as the second part.
+	 *
+	 * @see no.nordicsemi.android.dfu.DfuBaseService#EXTRA_PARTS_TOTAL
+	 */
+	public static final String EXTRA_PART_CURRENT = "no.nordicsemi.android.dfu.extra.EXTRA_PART_CURRENT";
+	/**
+	 * Number of parts in total.
+	 *
+	 * @see no.nordicsemi.android.dfu.DfuBaseService#EXTRA_PART_CURRENT
+	 */
+	public static final String EXTRA_PARTS_TOTAL = "no.nordicsemi.android.dfu.extra.EXTRA_PARTS_TOTAL";
+	/**
+	 * The current upload speed in bytes/millisecond.
+	 */
+	public static final String EXTRA_SPEED_B_PER_MS = "no.nordicsemi.android.dfu.extra.EXTRA_SPEED_B_PER_MS";
+	/**
+	 * The average upload speed in bytes/millisecond for the current part.
+	 */
+	public static final String EXTRA_AVG_SPEED_B_PER_MS = "no.nordicsemi.android.dfu.extra.EXTRA_AVG_SPEED_B_PER_MS";
+	/**
+	 * The broadcast message contains the following extras:
+	 * <ul>
+	 * <li>{@link #EXTRA_DATA} - the progress value (percentage 0-100) or:
+	 * <ul>
+	 * <li>{@link #PROGRESS_CONNECTING}</li>
+	 * <li>{@link #PROGRESS_STARTING}</li>
+	 * <li>{@link #PROGRESS_ENABLING_DFU_MODE}</li>
+	 * <li>{@link #PROGRESS_VALIDATING}</li>
+	 * <li>{@link #PROGRESS_DISCONNECTING}</li>
+	 * <li>{@link #PROGRESS_COMPLETED}</li>
+	 * <li>{@link #PROGRESS_ABORTED}</li>
+	 * </ul>
+	 * </li>
+	 * <li>{@link #EXTRA_DEVICE_ADDRESS} - the target device address</li>
+	 * <li>{@link #EXTRA_PART_CURRENT} - the number of currently transmitted part</li>
+	 * <li>{@link #EXTRA_PARTS_TOTAL} - total number of parts that are being sent, f.e. if a ZIP file contains a Soft Device, a Bootloader and an Application,
+	 * the SoftDevice and Bootloader will be send together as one part. Then the service will disconnect and reconnect to the new Bootloader and send the
+	 * application as part number two.</li>
+	 * <li>{@link #EXTRA_SPEED_B_PER_MS} - current speed in bytes/millisecond as float</li>
+	 * <li>{@link #EXTRA_AVG_SPEED_B_PER_MS} - the average transmission speed in bytes/millisecond as float</li>
+	 * </ul>
+	 */
+	public static final String BROADCAST_PROGRESS = "no.nordicsemi.android.dfu.broadcast.BROADCAST_PROGRESS";
+	/**
+	 * Service is connecting to the remote DFU target.
+	 */
+	public static final int PROGRESS_CONNECTING = -1;
+	/**
+	 * Service is enabling notifications and starting transmission.
+	 */
+	public static final int PROGRESS_STARTING = -2;
+	/**
+	 * Service has triggered a switch to bootloader mode. Now the service waits for the link loss event (this may take up to several seconds) and will connect again
+	 * to the same device, now started in the bootloader mode.
+	 */
+	public static final int PROGRESS_ENABLING_DFU_MODE = -3;
+	/**
+	 * Service is sending validation request to the remote DFU target.
+	 */
+	public static final int PROGRESS_VALIDATING = -4;
+	/**
+	 * Service is disconnecting from the DFU target.
+	 */
+	public static final int PROGRESS_DISCONNECTING = -5;
+	/**
+	 * The connection is successful.
+	 */
+	public static final int PROGRESS_COMPLETED = -6;
+	/**
+	 * The upload has been aborted. Previous software version will be restored on the target.
+	 */
+	public static final int PROGRESS_ABORTED = -7;
+	/**
+	 * The broadcast error message contains the following extras:
+	 * <ul>
+	 * <li>{@link #EXTRA_DATA} - the error number. Use {@link GattError#parse(int)} to get String representation</li>
+	 * <li>{@link #EXTRA_DEVICE_ADDRESS} - the target device address</li>
+	 * </ul>
+	 */
+	public static final String BROADCAST_ERROR = "no.nordicsemi.android.dfu.broadcast.BROADCAST_ERROR";
+	/**
+	 * The type of the error. This extra contains information about that kind of error has occurred. Connection state errors and other errors may share the same numbers.
+	 * For example, the {@link BluetoothGattCallback#onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int)} method may return a status code 8 (GATT INSUF AUTHORIZATION),
+	 * while the status code 8 returned by {@link BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)} is a GATT CONN TIMEOUT error.
+	 */
+	public static final String EXTRA_ERROR_TYPE = "no.nordicsemi.android.dfu.extra.EXTRA_ERROR_TYPE";
+	public static final int ERROR_TYPE_OTHER = 0;
+	public static final int ERROR_TYPE_COMMUNICATION_STATE = 1;
+	public static final int ERROR_TYPE_COMMUNICATION = 2;
+	public static final int ERROR_TYPE_DFU_REMOTE = 3;
+	/**
+	 * If this bit is set than the progress value indicates an error. Use {@link GattError#parse(int)} to obtain error name.
+	 */
+	public static final int ERROR_MASK = 0x1000;
+	public static final int ERROR_DEVICE_DISCONNECTED = ERROR_MASK; // | 0x00;
+	public static final int ERROR_FILE_NOT_FOUND = ERROR_MASK | 0x01;
+	/**
+	 * Thrown if service was unable to open the file ({@link java.io.IOException} has been thrown).
+	 */
+	public static final int ERROR_FILE_ERROR = ERROR_MASK | 0x02;
+	/**
+	 * Thrown when input file is not a valid HEX or ZIP file.
+	 */
+	public static final int ERROR_FILE_INVALID = ERROR_MASK | 0x03;
+	/**
+	 * Thrown when {@link java.io.IOException} occurred when reading from file.
+	 */
+	public static final int ERROR_FILE_IO_EXCEPTION = ERROR_MASK | 0x04;
+	/**
+	 * Error thrown when {@code gatt.discoverServices();} returns false.
+	 */
+	public static final int ERROR_SERVICE_DISCOVERY_NOT_STARTED = ERROR_MASK | 0x05;
+	/**
+	 * Thrown when the service discovery has finished but the DFU service has not been found. The device does not support DFU of is not in DFU mode.
+	 */
+	public static final int ERROR_SERVICE_NOT_FOUND = ERROR_MASK | 0x06;
+	/**
+	 * Thrown when the required DFU service has been found but at least one of the DFU characteristics is absent.
+	 */
+	public static final int ERROR_CHARACTERISTICS_NOT_FOUND = ERROR_MASK | 0x07;
+	/**
+	 * Thrown when unknown response has been obtained from the target. The DFU target must follow specification.
+	 */
+	public static final int ERROR_INVALID_RESPONSE = ERROR_MASK | 0x08;
+	/**
+	 * Thrown when the the service does not support given type or mime-type.
+	 */
+	public static final int ERROR_FILE_TYPE_UNSUPPORTED = ERROR_MASK | 0x09;
+	/**
+	 * Thrown when the the Bluetooth adapter is disabled.
+	 */
+	public static final int ERROR_BLUETOOTH_DISABLED = ERROR_MASK | 0x0A;
+	/**
+	 * DFU Bootloader version 0.6+ requires sending the Init packet. If such bootloader version is detected, but the init packet has not been set this error is thrown.
+	 */
+	public static final int ERROR_INIT_PACKET_REQUIRED = ERROR_MASK | 0x0B;
+    /**
+     * Thrown when the firmware file is not word-aligned. The firmware size must be dividable by 4 bytes.
+     */
+    public static final int ERROR_FILE_SIZE_INVALID = ERROR_MASK | 0x0C;
+	/**
+	 * Flag set then the DFU target returned a DFU error. Look for DFU specification to get error codes.
+	 */
+	public static final int ERROR_REMOTE_MASK = 0x2000;
+	/**
+	 * The flag set when one of {@link android.bluetooth.BluetoothGattCallback} methods was called with status other than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
+	 */
+	public static final int ERROR_CONNECTION_MASK = 0x4000;
+	/**
+	 * The flag set when the {@link android.bluetooth.BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} method was called with
+	 * status other than {@link android.bluetooth.BluetoothGatt#GATT_SUCCESS}.
+	 */
+	public static final int ERROR_CONNECTION_STATE_MASK = 0x8000;
+	/**
+	 * The log events are only broadcast when there is no nRF Logger installed. The broadcast contains 2 extras:
+	 * <ul>
+	 * <li>{@link #EXTRA_LOG_LEVEL} - The log level, one of following: {@link #LOG_LEVEL_DEBUG}, {@link #LOG_LEVEL_VERBOSE}, {@link #LOG_LEVEL_INFO},
+	 * {@link #LOG_LEVEL_APPLICATION}, {@link #LOG_LEVEL_WARNING}, {@link #LOG_LEVEL_ERROR}</li>
+	 * <li>{@link #EXTRA_LOG_MESSAGE}</li> - The log message
+	 * </ul>
+	 */
+	public static final String BROADCAST_LOG = "no.nordicsemi.android.dfu.broadcast.BROADCAST_LOG";
+	public static final String EXTRA_LOG_MESSAGE = "no.nordicsemi.android.dfu.extra.EXTRA_LOG_INFO";
+	public static final String EXTRA_LOG_LEVEL = "no.nordicsemi.android.dfu.extra.EXTRA_LOG_LEVEL";
+	/*
+	 * Note:
+	 * The nRF Logger API library has been excluded from the DfuLibrary.
+	 * All log events are now being sent using local broadcasts and may be logged into nRF Logger in the app module.
+	 * This is to make the Dfu module independent from logging tool.
+	 *
+	 * The log levels below are equal to log levels in nRF Logger API library, v 2.0.
+	 * @see https://github.com/NordicSemiconductor/nRF-Logger-API
+	 */
+	/**
+	 * Level used just for debugging purposes. It has lowest level
+	 */
+	public final static int LOG_LEVEL_DEBUG = 0;
+	/**
+	 * Log entries with minor importance
+	 */
+	public final static int LOG_LEVEL_VERBOSE = 1;
+	/**
+	 * Default logging level for important entries
+	 */
+	public final static int LOG_LEVEL_INFO = 5;
+	/**
+	 * Log entries level for applications
+	 */
+	public final static int LOG_LEVEL_APPLICATION = 10;
+	/**
+	 * Log entries with high importance
+	 */
+	public final static int LOG_LEVEL_WARNING = 15;
+	/**
+	 * Log entries with very high importance, like errors
+	 */
+	public final static int LOG_LEVEL_ERROR = 20;
+	/**
+	 * Activity may broadcast this broadcast in order to pause, resume or abort DFU process.
+	 * Use {@link #EXTRA_ACTION} extra to pass the action.
+	 */
+	public static final String BROADCAST_ACTION = "no.nordicsemi.android.dfu.broadcast.BROADCAST_ACTION";
+	/**
+	 * The action extra. It may have one of the following values: {@link #ACTION_PAUSE}, {@link #ACTION_RESUME}, {@link #ACTION_ABORT}.
+	 */
+	public static final String EXTRA_ACTION = "no.nordicsemi.android.dfu.extra.EXTRA_ACTION";
+	/** Pauses the upload. The service will wait for broadcasts with the action set to {@link #ACTION_RESUME} or {@link #ACTION_ABORT}. */
+	public static final int ACTION_PAUSE = 0;
+	/** Resumes the upload that has been paused before using {@link #ACTION_PAUSE}. */
+	public static final int ACTION_RESUME = 1;
+	/**
+	 * Aborts the upload. The service does not need to be paused before.
+	 * After sending {@link #BROADCAST_ACTION} with extra {@link #EXTRA_ACTION} set to this value the DFU bootloader will restore the old application
+	 * (if there was already an application). Be aware that uploading the Soft Device will erase the application in order to make space in the memory.
+	 * In case there is no application, or the application has been removed, the DFU bootloader will be started and user may try to send the application again.
+	 * The bootloader may advertise with the address incremented by 1 to prevent caching services.
+	 */
+	public static final int ACTION_ABORT = 2;
+
+	// DFU status values
+	public static final int DFU_STATUS_SUCCESS = 1;
+	public static final int DFU_STATUS_INVALID_STATE = 2;
+	public static final int DFU_STATUS_NOT_SUPPORTED = 3;
+	public static final int DFU_STATUS_DATA_SIZE_EXCEEDS_LIMIT = 4;
+	public static final int DFU_STATUS_CRC_ERROR = 5;
+	public static final int DFU_STATUS_OPERATION_FAILED = 6;
+	// Operation codes and packets
+	private static final int OP_CODE_START_DFU_KEY = 0x01; // 1
+	private static final int OP_CODE_INIT_DFU_PARAMS_KEY = 0x02; // 2
+	private static final int OP_CODE_RECEIVE_FIRMWARE_IMAGE_KEY = 0x03; // 3
+	private static final int OP_CODE_VALIDATE_KEY = 0x04; // 4
+	private static final int OP_CODE_ACTIVATE_AND_RESET_KEY = 0x05; // 5
+	private static final int OP_CODE_RESET_KEY = 0x06; // 6
+	//private static final int OP_CODE_PACKET_REPORT_RECEIVED_IMAGE_SIZE_KEY = 0x07; // 7
+	private static final int OP_CODE_PACKET_RECEIPT_NOTIF_REQ_KEY = 0x08; // 8
+	private static final int OP_CODE_RESPONSE_CODE_KEY = 0x10; // 16
+	private static final int OP_CODE_PACKET_RECEIPT_NOTIF_KEY = 0x11; // 11
+	private static final byte[] OP_CODE_START_DFU = new byte[]{OP_CODE_START_DFU_KEY, 0x00};
+	private static final byte[] OP_CODE_INIT_DFU_PARAMS_START = new byte[]{OP_CODE_INIT_DFU_PARAMS_KEY, 0x00};
+	private static final byte[] OP_CODE_INIT_DFU_PARAMS_COMPLETE = new byte[]{OP_CODE_INIT_DFU_PARAMS_KEY, 0x01};
+	private static final byte[] OP_CODE_RECEIVE_FIRMWARE_IMAGE = new byte[]{OP_CODE_RECEIVE_FIRMWARE_IMAGE_KEY};
+	private static final byte[] OP_CODE_VALIDATE = new byte[]{OP_CODE_VALIDATE_KEY};
+	private static final byte[] OP_CODE_ACTIVATE_AND_RESET = new byte[]{OP_CODE_ACTIVATE_AND_RESET_KEY};
+	private static final byte[] OP_CODE_RESET = new byte[]{OP_CODE_RESET_KEY};
+	//private static final byte[] OP_CODE_REPORT_RECEIVED_IMAGE_SIZE = new byte[] { OP_CODE_PACKET_REPORT_RECEIVED_IMAGE_SIZE_KEY };
+	private static final byte[] OP_CODE_PACKET_RECEIPT_NOTIF_REQ = new byte[]{OP_CODE_PACKET_RECEIPT_NOTIF_REQ_KEY, 0x00, 0x00};
+
+	// UUIDs used by the DFU
+	private static final UUID GENERIC_ATTRIBUTE_SERVICE_UUID = new UUID(0x0000180100001000l, 0x800000805F9B34FBl);
+	private static final UUID SERVICE_CHANGED_UUID = new UUID(0x00002A0500001000l, 0x800000805F9B34FBl);
+	private static final UUID DFU_SERVICE_UUID = new UUID(0x000015301212EFDEl, 0x1523785FEABCD123l);
+	private static final UUID DFU_CONTROL_POINT_UUID = new UUID(0x000015311212EFDEl, 0x1523785FEABCD123l);
+	private static final UUID DFU_PACKET_UUID = new UUID(0x000015321212EFDEl, 0x1523785FEABCD123l);
+	private static final UUID DFU_VERSION = new UUID(0x000015341212EFDEl, 0x1523785FEABCD123l);
+	private static final UUID CLIENT_CHARACTERISTIC_CONFIG = new UUID(0x0000290200001000l, 0x800000805f9b34fbl);
+	//
+	public static final int NOTIFICATION_ID = 283; // a random number
+	private static final int NOTIFICATIONS = 1;
+	private static final int INDICATIONS = 2;
+	private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+	private static final int MAX_PACKET_SIZE = 20; // the maximum number of bytes in one packet is 20. May be less.
+	private final byte[] mBuffer = new byte[MAX_PACKET_SIZE];
+	/**
+	 * Lock used in synchronization purposes
+	 */
+	private final Object mLock = new Object();
+	private BluetoothAdapter mBluetoothAdapter;
+	private InputStream mInputStream;
+	private String mDeviceAddress;
+	private String mDeviceName;
+	private boolean mDisableNotification;
+	/**
+	 * The current connection state. If its value is > 0 than an error has occurred. Error number is a negative value of mConnectionState
+	 */
+	private int mConnectionState;
+	private final static int STATE_DISCONNECTED = 0;
+	private final static int STATE_CONNECTING = -1;
+	private final static int STATE_CONNECTED = -2;
+	private final static int STATE_CONNECTED_AND_READY = -3; // indicates that services were discovered
+	private final static int STATE_DISCONNECTING = -4;
+	private final static int STATE_CLOSED = -5;
+	/**
+	 * The number of the last error that has occurred or 0 if there was no error
+	 */
+	private int mError;
+	/**
+	 * Flag set when we got confirmation from the device that notifications are enabled.
+	 */
+	private boolean mNotificationsEnabled;
+	/**
+	 * Flag set when we got confirmation from the device that Service Changed indications are enabled.
+	 */
+	private boolean mServiceChangedIndicationsEnabled;
+	/**
+	 * The number of packets of firmware data to be send before receiving a new Packets receipt notification. 0 disables the packets notifications
+	 */
+	private int mPacketsBeforeNotification = 10;
+	/**
+	 * Size of BIN content of all hex files that are going to be transmitted.
+	 */
+	private int mImageSizeInBytes;
+	/**
+	 * Number of bytes transmitted.
+	 */
+	private int mBytesSent;
+	/**
+	 * Number of bytes confirmed by the notification.
+	 */
+	@SuppressWarnings("unused")
+	private int mBytesConfirmed;
+	private int mPacketsSentSinceNotification;
+	/**
+	 * This value is used to calculate the current transfer speed.
+	 */
+	private int mLastBytesSent;
+	/**
+	 * Firmware update may require two connections: one for Soft Device and/or Bootloader upload and second for Application. This fields contains the current part number.
+	 */
+	private int mPartCurrent;
+	/**
+	 * Total number of parts.
+	 */
+	private int mPartsTotal;
+	private int mFileType;
+	private long mLastProgressTime, mStartTime;
+	/**
+	 * Flag sent when a request has been sent that will cause the DFU target to reset. Often, after sending such command, Android throws a connection state error. If this flag is set the error will be
+	 * ignored.
+	 */
+	private boolean mResetRequestSent;
+	/**
+	 * Flag indicating whether the image size has been already transferred or not
+	 */
+	private boolean mImageSizeSent;
+	/**
+	 * Flag indicating whether the init packet has been already transferred or not
+	 */
+	private boolean mInitPacketSent;
+	/**
+	 * Flag indicating whether the request was completed or not
+	 */
+	private boolean mRequestCompleted;
+	/**
+	 * <p>
+	 * Flag set to <code>true</code> when the DFU target had send a notification with status other than {@link #DFU_STATUS_SUCCESS}. Setting it to <code>true</code> will abort sending firmware and
+	 * stop logging notifications (read below for explanation).
+	 * </p>
+	 * <p>
+	 * The onCharacteristicWrite(..) callback is called when Android writes the packet into the outgoing queue, not when it physically sends the data.
+	 * This means that the service will first put up to N* packets, one by one, to the queue, while in fact the first one is transmitted.
+	 * In case the DFU target is in an invalid state it will notify Android with a notification 10-03-02 for each packet of firmware that has been sent.
+	 * After receiving the first such notification, the DFU service will add the reset command to the outgoing queue, but it will still be receiving such notifications
+	 * until all the data packets are sent. Those notifications should be ignored. This flag will prevent from logging "Notification received..." more than once.
+	 * </p>
+	 * <p>
+	 * Additionally, sometimes after writing the command 6 ({@link #OP_CODE_RESET}), Android will receive a notification and update the characteristic value with 10-03-02 and the callback for write
+	 * reset command will log "[DFU] Data written to ..., value (0x): 10-03-02" instead of "...(x0): 06". But this does not matter for the DFU process.
+	 * </p>
+	 * <p>
+	 * N* - Value of Packet Receipt Notification, 10 by default.
+	 * </p>
+	 */
+	private boolean mRemoteErrorOccurred;
+	/** Flag set to true if sending was paused. */
+	private boolean mPaused;
+	/** Flag set to true if sending was aborted. */
+	private boolean mAborted;
+	/**
+	 * Latest data received from device using notification.
+	 */
+	private byte[] mReceivedData = null;
+
+	private final BroadcastReceiver mConnectionStateBroadcastReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(final Context context, final Intent intent) {
+			// Obtain the device and check it this is the one that we are connected to
+			final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+			if (!device.getAddress().equals(mDeviceAddress))
+				return;
+
+			final String action = intent.getAction();
+
+			logi("Action received: " + action);
+			mConnectionState = STATE_DISCONNECTED;
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+	};
+
+	private final BroadcastReceiver mDfuActionReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(final Context context, final Intent intent) {
+			final int action = intent.getIntExtra(EXTRA_ACTION, 0);
+
+			switch (action) {
+				case ACTION_PAUSE:
+					mPaused = true;
+					break;
+				case ACTION_RESUME:
+					mPaused = false;
+
+					// Notify waiting thread
+					synchronized (mLock) {
+						mLock.notifyAll();
+					}
+					break;
+				case ACTION_ABORT:
+					mPaused = false;
+					mAborted = true;
+
+					// Notify waiting thread
+					synchronized (mLock) {
+						mLock.notifyAll();
+					}
+					break;
+			}
+		}
+	};
+
+	private final BroadcastReceiver mBondStateBroadcastReceiver = new BroadcastReceiver() {
+		@Override
+		public void onReceive(final Context context, final Intent intent) {
+			// Obtain the device and check it this is the one that we are connected to
+			final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+			if (!device.getAddress().equals(mDeviceAddress))
+				return;
+
+			// Read bond state
+			final int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
+			if (bondState == BluetoothDevice.BOND_BONDING)
+				return;
+
+			mRequestCompleted = true;
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+	};
+
+	private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
+		@Override
+		public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
+			// Check whether an error occurred
+			if (status == BluetoothGatt.GATT_SUCCESS) {
+				if (newState == BluetoothGatt.STATE_CONNECTED) {
+					logi("Connected to GATT server");
+					mConnectionState = STATE_CONNECTED;
+
+					/*
+					 *  The onConnectionStateChange callback is called just after establishing connection and before sending Encryption Request BLE event in case of a paired device.
+					 *  In that case and when the Service Changed CCCD is enabled we will get the indication after initializing the encryption, about 1600 milliseconds later.
+					 *  If we discover services right after connecting, the onServicesDiscovered callback will be called immediately, before receiving the indication and the following
+					 *  service discovery and we may end up with old, application's services instead.
+					 *
+					 *  This is to support the buttonless switch from application to bootloader mode where the DFU bootloader notifies the master about service change.
+					 *  Tested on Nexus 4 (Android 4.4.4 and 5), Nexus 5 (Android 5), Samsung Note 2 (Android 4.4.2). The time after connection to end of service discovery is about 1.6s
+					 *  on Samsung Note 2.
+					 *
+					 *  NOTE: We are doing this to avoid the hack with calling the hidden gatt.refresh() method, at least for bonded devices.
+					 */
+					if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_BONDED) {
+						try {
+							synchronized (this) {
+								logd("Waiting 1600 ms for a possible Service Changed indication...");
+								wait(1600);
+
+								// After 1.6s the services are already discovered so the following gatt.discoverServices() finishes almost immediately.
+
+								// NOTE: This also works with shorted waiting time. The gatt.discoverServices() must be called after the indication is received which is
+								// about 600ms after establishing connection. Values 600 - 1600ms should be OK.
+							}
+						} catch (InterruptedException e) {
+							// Do nothing
+						}
+					}
+
+					// Attempts to discover services after successful connection.
+					final boolean success = gatt.discoverServices();
+					logi("Attempting to start service discovery... " + (success ? "succeed" : "failed"));
+
+					if (!success) {
+						mError = ERROR_SERVICE_DISCOVERY_NOT_STARTED;
+					} else {
+						// Just return here, lock will be notified when service discovery finishes
+						return;
+					}
+				} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
+					logi("Disconnected from GATT server");
+					mPaused = false;
+					mConnectionState = STATE_DISCONNECTED;
+				}
+			} else {
+				loge("Connection state change error: " + status + " newState: " + newState);
+				if (newState == BluetoothGatt.STATE_DISCONNECTED)
+					mConnectionState = STATE_DISCONNECTED;
+				mPaused = false;
+				mError = ERROR_CONNECTION_STATE_MASK | status;
+			}
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+
+		@Override
+		public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
+			if (status == BluetoothGatt.GATT_SUCCESS) {
+				logi("Services discovered");
+				mConnectionState = STATE_CONNECTED_AND_READY;
+			} else {
+				loge("Service discovery error: " + status);
+				mError = ERROR_CONNECTION_MASK | status;
+			}
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+
+		@Override
+		public void onDescriptorRead(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
+			if (status == BluetoothGatt.GATT_SUCCESS) {
+				if (CLIENT_CHARACTERISTIC_CONFIG.equals(descriptor.getUuid())) {
+					if (SERVICE_CHANGED_UUID.equals(descriptor.getCharacteristic().getUuid())) {
+						// We have enabled indications for the Service Changed characteristic
+						mServiceChangedIndicationsEnabled = descriptor.getValue()[0] == 2;
+						mRequestCompleted = true;
+					}
+				}
+			} else {
+				loge("Descriptor read error: " + status);
+				mError = ERROR_CONNECTION_MASK | status;
+			}
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+
+		@Override
+		public void onDescriptorWrite(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
+			if (status == BluetoothGatt.GATT_SUCCESS) {
+				if (CLIENT_CHARACTERISTIC_CONFIG.equals(descriptor.getUuid())) {
+					if (SERVICE_CHANGED_UUID.equals(descriptor.getCharacteristic().getUuid())) {
+						// We have enabled indications for the Service Changed characteristic
+						mServiceChangedIndicationsEnabled = descriptor.getValue()[0] == 2;
+					} else {
+						// We have enabled notifications for this characteristic
+						mNotificationsEnabled = descriptor.getValue()[0] == 1;
+					}
+				}
+			} else {
+				loge("Descriptor write error: " + status);
+				mError = ERROR_CONNECTION_MASK | status;
+			}
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+
+		@Override
+		public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
+			if (status == BluetoothGatt.GATT_SUCCESS) {
+				/*
+				 * This method is called when either a CONTROL POINT or PACKET characteristic has been written.
+				 * If it is the CONTROL POINT characteristic, just set the {@link mRequestCompleted} flag to true. The main thread will continue its task when notified.
+				 * If the PACKET characteristic was written we must:
+				 * - if the image size was written in DFU Start procedure, just set flag to true
+				 * otherwise
+				 * - send the next packet, if notification is not required at that moment, or
+				 * - do nothing, because we have to wait for the notification to confirm the data received
+				 */
+				if (DFU_PACKET_UUID.equals(characteristic.getUuid())) {
+					if (mImageSizeSent && mInitPacketSent) {
+						// If the PACKET characteristic was written with image data, update counters
+						mBytesSent += characteristic.getValue().length;
+						mPacketsSentSinceNotification++;
+
+						// If a packet receipt notification is expected, or the last packet was sent, do nothing. There onCharacteristicChanged listener will catch either
+						// a packet confirmation (if there are more bytes to send) or the image received notification (it upload process was completed)
+						final boolean notificationExpected = mPacketsBeforeNotification > 0 && mPacketsSentSinceNotification == mPacketsBeforeNotification;
+						final boolean lastPacketTransferred = mBytesSent == mImageSizeInBytes;
+
+						if (notificationExpected || lastPacketTransferred)
+							return;
+
+						// When neither of them is true, send the next packet
+						try {
+							waitIfPaused();
+							// The writing might have been aborted (mAborted = true), an error might have occurred.
+							// In that case stop sending.
+							if (mAborted || mError != 0 || mRemoteErrorOccurred || mResetRequestSent) {
+								// notify waiting thread
+								synchronized (mLock) {
+									sendLogBroadcast(LOG_LEVEL_WARNING, "Upload terminated");
+									mLock.notifyAll();
+									return;
+								}
+							}
+
+							final byte[] buffer = mBuffer;
+							final int size = mInputStream.read(buffer);
+							writePacket(gatt, characteristic, buffer, size);
+							updateProgressNotification();
+							return;
+						} catch (final HexFileValidationException e) {
+							loge("Invalid HEX file");
+							mError = ERROR_FILE_INVALID;
+						} catch (final IOException e) {
+							loge("Error while reading the input stream", e);
+							mError = ERROR_FILE_IO_EXCEPTION;
+						}
+					} else if (!mImageSizeSent) {
+						// We've got confirmation that the image size was sent
+						sendLogBroadcast(LOG_LEVEL_INFO, "Data written to " + characteristic.getUuid() + ", value (0x): " + parse(characteristic));
+						mImageSizeSent = true;
+					} else {
+						// We've got confirmation that the init packet was sent
+						sendLogBroadcast(LOG_LEVEL_INFO, "Data written to " + characteristic.getUuid() + ", value (0x): " + parse(characteristic));
+						mInitPacketSent = true;
+					}
+				} else {
+					// If the CONTROL POINT characteristic was written just set the flag to true. The main thread will continue its task when notified.
+					sendLogBroadcast(LOG_LEVEL_INFO, "Data written to " + characteristic.getUuid() + ", value (0x): " + parse(characteristic));
+					mRequestCompleted = true;
+				}
+			} else {
+				/*
+				 * If a Reset (Op Code = 6) or Activate and Reset (Op Code = 5) commands are sent, the DFU target resets and sometimes does it so quickly that does not manage to send
+				 * any ACK to the controller and error 133 is thrown here. This bug should be fixed in SDK 8.0+ where the target would gracefully disconnect before restarting.
+				 */
+				if (mResetRequestSent)
+					mRequestCompleted = true;
+				else {
+					loge("Characteristic write error: " + status);
+					mError = ERROR_CONNECTION_MASK | status;
+				}
+			}
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+
+		@Override
+		public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
+			if (status == BluetoothGatt.GATT_SUCCESS) {
+				/*
+				 * This method is called when the DFU Version characteristic has been read.
+				 */
+				sendLogBroadcast(LOG_LEVEL_INFO, "Read Response received from " + characteristic.getUuid() + ", value (0x): " + parse(characteristic));
+				mReceivedData = characteristic.getValue();
+				mRequestCompleted = true;
+			} else {
+				loge("Characteristic read error: " + status);
+				mError = ERROR_CONNECTION_MASK | status;
+			}
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+
+		@Override
+		public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
+			final int responseType = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
+
+			switch (responseType) {
+				case OP_CODE_PACKET_RECEIPT_NOTIF_KEY:
+					final BluetoothGattCharacteristic packetCharacteristic = gatt.getService(DFU_SERVICE_UUID).getCharacteristic(DFU_PACKET_UUID);
+
+					try {
+						mBytesConfirmed = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 1);
+						mPacketsSentSinceNotification = 0;
+
+						waitIfPaused();
+						// The writing might have been aborted (mAborted = true), an error might have occurred.
+						// In that case quit sending.
+						if (mAborted || mError != 0 || mRemoteErrorOccurred || mResetRequestSent) {
+							sendLogBroadcast(LOG_LEVEL_WARNING, "Upload terminated");
+							break;
+						}
+
+						final byte[] buffer = mBuffer;
+						final int size = mInputStream.read(buffer);
+						writePacket(gatt, packetCharacteristic, buffer, size);
+						updateProgressNotification();
+						return;
+					} catch (final HexFileValidationException e) {
+						loge("Invalid HEX file");
+						mError = ERROR_FILE_INVALID;
+					} catch (final IOException e) {
+						loge("Error while reading the input stream", e);
+						mError = ERROR_FILE_IO_EXCEPTION;
+					}
+					break;
+				case OP_CODE_RESPONSE_CODE_KEY:
+				default:
+				/*
+				 * If the DFU target device is in invalid state (f.e. the Init Packet is required but has not been selected), the target will send DFU_STATUS_INVALID_STATE error
+				 * for each firmware packet that was send. We are interested may ignore all but the first one.
+				 * After obtaining a remote DFU error the OP_CODE_RESET_KEY will be sent.
+				 */
+					if (mRemoteErrorOccurred)
+						break;
+					final int status = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 2);
+					if (status != DFU_STATUS_SUCCESS)
+						mRemoteErrorOccurred = true;
+
+					sendLogBroadcast(LOG_LEVEL_INFO, "Notification received from " + characteristic.getUuid() + ", value (0x): " + parse(characteristic));
+					mReceivedData = characteristic.getValue();
+					break;
+			}
+
+			// Notify waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+
+		// This method is repeated here and in the service class for performance matters.
+		private String parse(final BluetoothGattCharacteristic characteristic) {
+			final byte[] data = characteristic.getValue();
+			if (data == null)
+				return "";
+			final int length = data.length;
+			if (length == 0)
+				return "";
+
+			final char[] out = new char[length * 3 - 1];
+			for (int j = 0; j < length; j++) {
+				int v = data[j] & 0xFF;
+				out[j * 3] = HEX_ARRAY[v >>> 4];
+				out[j * 3 + 1] = HEX_ARRAY[v & 0x0F];
+				if (j != length - 1)
+					out[j * 3 + 2] = '-';
+			}
+			return new String(out);
+		}
+	};
+	/**
+	 * Stores the last progress percent. Used to prevent from sending progress notifications with the same value.
+	 * @see #updateProgressNotification(int)
+	 */
+	private int mLastProgress = -1;
+
+	public DfuBaseService() {
+		super(TAG);
+	}
+
+	private static IntentFilter makeDfuActionIntentFilter() {
+		final IntentFilter intentFilter = new IntentFilter();
+		intentFilter.addAction(DfuBaseService.BROADCAST_ACTION);
+		return intentFilter;
+	}
+
+	@Override
+	public void onCreate() {
+		super.onCreate();
+
+		initialize();
+
+		final LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
+		final IntentFilter actionFilter = makeDfuActionIntentFilter();
+		manager.registerReceiver(mDfuActionReceiver, actionFilter);
+		registerReceiver(mDfuActionReceiver, actionFilter); // Additionally we must register this receiver as a non-local to get broadcasts from the notification actions
+
+		final IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+		registerReceiver(mConnectionStateBroadcastReceiver, filter);
+
+		final IntentFilter bondFilter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+		registerReceiver(mBondStateBroadcastReceiver, bondFilter);
+	}
+
+	@Override
+	public void onDestroy() {
+		super.onDestroy();
+
+		final LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
+		manager.unregisterReceiver(mDfuActionReceiver);
+
+		unregisterReceiver(mDfuActionReceiver);
+		unregisterReceiver(mConnectionStateBroadcastReceiver);
+		unregisterReceiver(mBondStateBroadcastReceiver);
+	}
+
+	@Override
+	protected void onHandleIntent(final Intent intent) {
+		final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+		// Read input parameters
+		final String deviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
+		final String deviceName = intent.getStringExtra(EXTRA_DEVICE_NAME);
+		final boolean disableNotification = intent.getBooleanExtra(EXTRA_DISABLE_NOTIFICATION, false);
+		final String filePath = intent.getStringExtra(EXTRA_FILE_PATH);
+		final Uri fileUri = intent.getParcelableExtra(EXTRA_FILE_URI);
+		final int fileResId = intent.getIntExtra(EXTRA_FILE_RES_ID, 0);
+		final String initFilePath = intent.getStringExtra(EXTRA_INIT_FILE_PATH);
+		final Uri initFileUri = intent.getParcelableExtra(EXTRA_INIT_FILE_URI);
+		final int initFileResId = intent.getIntExtra(EXTRA_INIT_FILE_RES_ID, 0);
+		int fileType = intent.getIntExtra(EXTRA_FILE_TYPE, TYPE_AUTO);
+		if (filePath != null && fileType == TYPE_AUTO)
+			fileType = filePath.toLowerCase(Locale.US).endsWith("zip") ? TYPE_AUTO : TYPE_APPLICATION;
+		String mimeType = intent.getStringExtra(EXTRA_FILE_MIME_TYPE);
+		mimeType = mimeType != null ? mimeType : (fileType == TYPE_AUTO ? MIME_TYPE_ZIP : MIME_TYPE_OCTET_STREAM);
+		mPartCurrent = intent.getIntExtra(EXTRA_PART_CURRENT, 1);
+		mPartsTotal = intent.getIntExtra(EXTRA_PARTS_TOTAL, 1);
+
+		// Check file type and mime-type
+		if ((fileType & ~(TYPE_SOFT_DEVICE | TYPE_BOOTLOADER | TYPE_APPLICATION)) > 0 || !(MIME_TYPE_ZIP.equals(mimeType) || MIME_TYPE_OCTET_STREAM.equals(mimeType))) {
+			logw("File type or file mime-type not supported");
+			sendLogBroadcast(LOG_LEVEL_WARNING, "File type or file mime-type not supported");
+			sendErrorBroadcast(ERROR_FILE_TYPE_UNSUPPORTED);
+			return;
+		}
+		if (MIME_TYPE_OCTET_STREAM.equals(mimeType) && fileType != TYPE_SOFT_DEVICE && fileType != TYPE_BOOTLOADER && fileType != TYPE_APPLICATION) {
+			logw("Unable to determine file type");
+			sendLogBroadcast(LOG_LEVEL_WARNING, "Unable to determine file type");
+			sendErrorBroadcast(ERROR_FILE_TYPE_UNSUPPORTED);
+			return;
+		}
+
+		mDeviceAddress = deviceAddress;
+		mDeviceName = deviceName;
+		mDisableNotification = disableNotification;
+		mConnectionState = STATE_DISCONNECTED;
+		mBytesSent = 0;
+		mBytesConfirmed = 0;
+		mPacketsSentSinceNotification = 0;
+		mError = 0;
+		mLastProgressTime = 0;
+		mAborted = false;
+		mPaused = false;
+		mNotificationsEnabled = false;
+		mResetRequestSent = false;
+		mRequestCompleted = false;
+		mImageSizeSent = false;
+		mRemoteErrorOccurred = false;
+
+		// Read preferences
+		final boolean packetReceiptNotificationEnabled = preferences.getBoolean(DfuSettingsConstants.SETTINGS_PACKET_RECEIPT_NOTIFICATION_ENABLED, true);
+		String value = preferences.getString(DfuSettingsConstants.SETTINGS_NUMBER_OF_PACKETS, String.valueOf(DfuSettingsConstants.SETTINGS_NUMBER_OF_PACKETS_DEFAULT));
+		int numberOfPackets;
+		try {
+			numberOfPackets = Integer.parseInt(value);
+			if (numberOfPackets < 0 || numberOfPackets > 0xFFFF)
+				numberOfPackets = DfuSettingsConstants.SETTINGS_NUMBER_OF_PACKETS_DEFAULT;
+		} catch (final NumberFormatException e) {
+			numberOfPackets = DfuSettingsConstants.SETTINGS_NUMBER_OF_PACKETS_DEFAULT;
+		}
+		if (!packetReceiptNotificationEnabled)
+			numberOfPackets = 0;
+
+		// TODO(mcchou): Hack
+		mPacketsBeforeNotification = 2;
+
+		// The Soft Device starts where MBR ends (by default from the address 0x1000). Before there is a MBR section, which should not be transmitted over DFU.
+		// Applications and bootloader starts from bigger address. However, in custom DFU implementations, user may want to transmit the whole whole data, even from address 0x0000.
+		value = preferences.getString(DfuSettingsConstants.SETTINGS_MBR_SIZE, String.valueOf(DfuSettingsConstants.SETTINGS_DEFAULT_MBR_SIZE));
+		int mbrSize;
+		try {
+			mbrSize = Integer.parseInt(value);
+			if (mbrSize < 0)
+				mbrSize = 0;
+		} catch (final NumberFormatException e) {
+			mbrSize = DfuSettingsConstants.SETTINGS_DEFAULT_MBR_SIZE;
+		}
+		/*
+		 * In case of old DFU bootloader versions, where there was no DFU Version characteristic, the service was unable to determine whether it was in the application mode, or in
+		 * bootloader mode. In that case, if the following boolean value is set to false (default) the bootloader will count number of services on the device. In case of 3 service
+		 * it will start the DFU procedure (Generic Access, Generic Attribute, DFU Service). If more services will be found, it assumes that a jump to the DFU bootloader is required.
+		 *
+		 * However, in some cases, the DFU bootloader is used to flash firmware on other chip via nRF5x. In that case the application may support DFU operation without switching
+		 * to the bootloader mode itself.
+		 *
+		 * For newer implementations of DFU in such case the DFU Version should return value other than 0x0100 (major 0, minor 1) which means that the application does not support
+		 * DFU process itself but rather support jump to the bootloader mode.
+		 */
+		final boolean assumeDfuMode = preferences.getBoolean(DfuSettingsConstants.SETTINGS_ASSUME_DFU_NODE, false);
+
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Starting DFU service");
+
+		/*
+		 * First the service is trying to read the firmware and init packet files.
+		 */
+		InputStream is = null;
+		InputStream initIs = null;
+		int imageSizeInBytes;
+		try {
+			// Prepare data to send, calculate stream size
+			try {
+				sendLogBroadcast(LOG_LEVEL_VERBOSE, "Opening file...");
+				if (fileUri != null) {
+					is = openInputStream(fileUri, mimeType, mbrSize, fileType);
+				} else if (filePath != null) {
+					is = openInputStream(filePath, mimeType, mbrSize, fileType);
+				} else if (fileResId > 0) {
+					is = openInputStream(fileResId, mimeType, mbrSize, fileType);
+				}
+
+				if (initFileUri != null) {
+					// Try to read the Init Packet file from URI
+					initIs = getContentResolver().openInputStream(initFileUri);
+				} else if (initFilePath != null) {
+					// Try to read the Init Packet file from path
+					initIs = new FileInputStream(initFilePath);
+				} else if (initFileResId > 0) {
+					// Try to read the Init Packet file from given resource
+					initIs = getResources().openRawResource(initFileResId);
+				}
+
+				mInputStream = is;
+				imageSizeInBytes = mImageSizeInBytes = is.available();
+
+                if ((imageSizeInBytes % 4) != 0)
+                    throw new SizeValidationException("The new firmware is not word-aligned.");
+
+				// Update the file type bit field basing on the ZIP content
+				if (fileType == TYPE_AUTO && MIME_TYPE_ZIP.equals(mimeType)) {
+					final ArchiveInputStream zhis = (ArchiveInputStream) is;
+					fileType = zhis.getContentType();
+				}
+				mFileType = fileType;
+				// Set the Init packet stream in case of a ZIP file
+				if (MIME_TYPE_ZIP.equals(mimeType)) {
+					final ArchiveInputStream zhis = (ArchiveInputStream) is;
+
+                    // Validate sizes
+                    if ((fileType & TYPE_APPLICATION) > 0 && (zhis.applicationImageSize() % 4) != 0)
+                        throw new SizeValidationException("Application firmware is not word-aligned.");
+                    if ((fileType & TYPE_BOOTLOADER) > 0 && (zhis.bootloaderImageSize() % 4) != 0)
+                        throw new SizeValidationException("Bootloader firmware is not word-aligned.");
+                    if ((fileType & TYPE_SOFT_DEVICE) > 0 && (zhis.softDeviceImageSize() % 4) != 0)
+                        throw new SizeValidationException("Soft Device firmware is not word-aligned.");
+
+					if (fileType == TYPE_APPLICATION) {
+						if (zhis.getApplicationInit() != null)
+							initIs = new ByteArrayInputStream(zhis.getApplicationInit());
+					} else {
+						if (zhis.getSystemInit() != null)
+							initIs = new ByteArrayInputStream(zhis.getSystemInit());
+					}
+				}
+				sendLogBroadcast(LOG_LEVEL_INFO, "Image file opened (" + mImageSizeInBytes + " bytes in total)");
+			} catch (final SecurityException e) {
+				loge("A security exception occurred while opening file", e);
+				updateProgressNotification(ERROR_FILE_NOT_FOUND);
+				return;
+			} catch (final FileNotFoundException e) {
+				loge("An exception occurred while opening file", e);
+				updateProgressNotification(ERROR_FILE_NOT_FOUND);
+				return;
+            } catch (final SizeValidationException e) {
+                loge("Firmware not word-aligned", e);
+                updateProgressNotification(ERROR_FILE_SIZE_INVALID);
+                return;
+			} catch (final IOException e) {
+				loge("An exception occurred while calculating file size", e);
+				updateProgressNotification(ERROR_FILE_ERROR);
+				return;
+			} catch (final Exception e) {
+				loge("An exception occurred while opening files. Did you set the firmware file?", e);
+				updateProgressNotification(ERROR_FILE_ERROR);
+				return;
+			}
+
+			/*
+			 * Now let's connect to the device.
+			 * All the methods below are synchronous. The mLock object is used to wait for asynchronous calls.
+			 */
+			sendLogBroadcast(LOG_LEVEL_VERBOSE, "Connecting to DFU target...");
+			updateProgressNotification(PROGRESS_CONNECTING);
+
+			final BluetoothGatt gatt = connect(deviceAddress);
+			// Are we connected?
+			if (gatt == null) {
+				loge("Bluetooth adapter disabled");
+				sendLogBroadcast(LOG_LEVEL_ERROR, "Bluetooth adapter disabled");
+				updateProgressNotification(ERROR_BLUETOOTH_DISABLED);
+				return;
+			}
+			if (mError > 0) { // error occurred
+				final int error = mError & ~ERROR_CONNECTION_STATE_MASK;
+				loge("An error occurred while connecting to the device:" + error);
+				sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Connection failed (0x%02X): %s", error, GattError.parseConnectionError(error)));
+				terminateConnection(gatt, mError);
+				return;
+			}
+			if (mAborted) {
+				logi("Upload aborted");
+				sendLogBroadcast(LOG_LEVEL_WARNING, "Upload aborted");
+				terminateConnection(gatt, PROGRESS_ABORTED);
+				return;
+			}
+
+			// We have connected to DFU device and services are discoverer
+			final BluetoothGattService dfuService = gatt.getService(DFU_SERVICE_UUID); // there was a case when the service was null. I don't know why
+			if (dfuService == null) {
+				loge("DFU service does not exists on the device");
+				sendLogBroadcast(LOG_LEVEL_WARNING, "Connected. DFU Service not found");
+				terminateConnection(gatt, ERROR_SERVICE_NOT_FOUND);
+				return;
+			}
+			final BluetoothGattCharacteristic controlPointCharacteristic = dfuService.getCharacteristic(DFU_CONTROL_POINT_UUID);
+			final BluetoothGattCharacteristic packetCharacteristic = dfuService.getCharacteristic(DFU_PACKET_UUID);
+			if (controlPointCharacteristic == null || packetCharacteristic == null) {
+				loge("DFU characteristics not found in the DFU service");
+				sendLogBroadcast(LOG_LEVEL_WARNING, "Connected. DFU Characteristics not found");
+				terminateConnection(gatt, ERROR_CHARACTERISTICS_NOT_FOUND);
+				return;
+			}
+			/*
+			 * The DFU Version characteristic has been added in SDK 7.0.
+			 *
+			 * It may return version number in 2 bytes (f.e. 0x05-00), where the first one is the minor version and the second one is the major version.
+			 * In case of 0x05-00 the DFU has the version 0.5.
+			 *
+			 * Currently the following version numbers are supported:
+			 *
+			 *   - 0.1 (0x01-00) - The service is connected to the device in application mode, not to the DFU Bootloader. The application supports Long Term Key (LTK)
+			 *                     sharing and buttonless update. Enable notifications on the DFU Control Point characteristic and write 0x01-04 into it to jump to the Bootloader.
+			 *                     Check the Bootloader version again for more info about the Bootloader version.
+			 *
+			 *   - 0.5 (0x05-00) - The device is in the OTA-DFU Bootloader mode. The Bootloader supports LTK sharing and requires the Extended Init Packet. It supports
+			 *                     a SoftDevice, Bootloader or an Application update. SoftDevice and a Bootloader may be sent together.
+			 *
+			 *   - 0.6 (0x06-00) - The device is in the OTA-DFU Bootloader mode. The DFU Bootloader is from SDK 8.0 and has the same features as version 0.5. It also
+			 *                     supports also sending Service Changed notification in application mode after successful or aborted upload so no refreshing services is required.
+			 */
+			final BluetoothGattCharacteristic versionCharacteristic = dfuService.getCharacteristic(DFU_VERSION); // this may be null for older versions of the Bootloader
+
+			sendLogBroadcast(LOG_LEVEL_INFO, "Connected. Services discovered");
+			try {
+				updateProgressNotification(PROGRESS_STARTING);
+
+				/*
+				 * Read the version number if available.
+				 * The version number consists of 2 bytes: major and minor. Therefore f.e. the version 5 (00-05) can be read as 0.5.
+				 *
+				 * Currently supported versions are:
+				 *  * no DFU Version characteristic - we may be either in the bootloader mode or in the app mode. The DFU Bootloader from SDK 6.1 did not have this characteristic,
+				 *                                    but it also supported the buttonless update. Usually, the application must have had some additional services (like Heart Rate, etc)
+				 *                                    so if the number of services greater is than 3 (Generic Access, Generic Attribute, DFU Service) we can also assume we are in
+				 *                                    the application mode and jump is required.
+				 *
+				 *  * version = 1 (major = 0, minor = 1) - Application with DFU buttonless update supported. A jump to DFU mode is required.
+				 *
+				 *  * version = 5 (major = 0, minor = 5) - Since version 5 the Extended Init Packet is required. Keep in mind that if we are in the app mode the DFU Version characteristic
+				 *  								  still returns version = 1, as it is independent from the DFU Bootloader. The version = 5 is reported only after successful jump to
+				 *  								  the DFU mode. In version = 5 the bond information is always lost. Released in SDK 7.0.0.
+				 *
+				 *  * version = 6 (major = 0, minor = 6) - The DFU Bootloader may be configured to keep the bond information after application update. Please, see the {@link #EXTRA_KEEP_BOND}
+				 *  								  documentation for more information about how to enable the feature (disabled by default). A change in the DFU bootloader source and
+				 *  								  setting the {@link DfuServiceInitiator#setKeepBond} to true is required. Released in SDK 8.0.0.
+				 *
+				 *  * version = 7 (major = 0, minor = 7) - The SHA-256 firmware hash is used in the Extended Init Packet instead of CRC-16. This feature is transparent for the DFU Service.
+				 *
+				 *  * version = 8 (major = 0, minor = 8) - The Extended Init Packet is signed using the private key. The bootloader, using the public key, is able to verify the content.
+				 *  								  Released in SDK 9.0.0 as experimental feature.
+				 *  								  Caution! The firmware type (Application, Bootloader, SoftDevice or SoftDevice+Bootloader) is not encrypted as it is not a part of the
+				 *  								  Extended Init Packet. A change in the protocol will be required to fix this issue.
+				 */
+				int version = 0;
+				if (versionCharacteristic != null) {
+					version = readVersion(gatt, versionCharacteristic);
+					final int minor = (version & 0x0F);
+					final int major = (version >> 8);
+					logi("Version number read: " + major + "." + minor);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Version number read: " + major + "." + minor);
+				}
+
+				/*
+				 *  Check if we are in the DFU Bootloader or in the Application that supports the buttonless update.
+				 *
+				 *  In the DFU from SDK 6.1, which was also supporting the buttonless update, there was no DFU Version characteristic. In that case we may find out whether
+				 *  we are in the bootloader or application by simply checking the number of characteristics. This may be overridden by setting the DfuSettingsConstants.SETTINGS_ASSUME_DFU_NODE
+				 *  property to true in Shared Preferences.
+				 */
+				if (version == 1 || (!assumeDfuMode && version == 0 && gatt.getServices().size() > 3 /* No DFU Version char but more services than Generic Access, Generic Attribute, DFU Service */)) {
+					// The service is connected to the application, not to the bootloader
+					logw("Application with buttonless update found");
+					sendLogBroadcast(LOG_LEVEL_WARNING, "Application with buttonless update found");
+
+					// If we are bonded we may want to enable Service Changed characteristic indications.
+					// Note: This feature will be introduced in the SDK 8.0 as this is the proper way to refresh attribute list on the phone.
+					boolean hasServiceChanged = false;
+					if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_BONDED) {
+						final BluetoothGattService genericAttributeService = gatt.getService(GENERIC_ATTRIBUTE_SERVICE_UUID);
+						if (genericAttributeService != null) {
+							final BluetoothGattCharacteristic serviceChangedCharacteristic = genericAttributeService.getCharacteristic(SERVICE_CHANGED_UUID);
+							if (serviceChangedCharacteristic != null) {
+								// Let's read the current value of the Service Changed CCCD
+								final boolean serviceChangedIndicationsEnabled = isServiceChangedCCCDEnabled(gatt, serviceChangedCharacteristic);
+
+								if (!serviceChangedIndicationsEnabled) {
+									enableCCCD(gatt, serviceChangedCharacteristic, INDICATIONS);
+									sendLogBroadcast(LOG_LEVEL_APPLICATION, "Service Changed indications enabled");
+
+									/*
+									 * NOTE: The DFU Bootloader from SDK 8.0 (v0.6 and 0.5) has the following issue:
+									 *
+									 * When the central device (phone) connects to a bonded device (or connects and bonds) which supports the Service Changed characteristic,
+									 * but does not have the Service Changed indications enabled, the phone must enable them, disconnect and reconnect before starting the
+									 * DFU operation. This is because the current version of the Soft Device saves the ATT table on the DISCONNECTED event.
+									 * Sending the "jump to Bootloader" command (0x01-04) will cause the disconnect followed be a reset. The Soft Device does not
+									 * have time to store the ATT table on Flash memory before the reset.
+									 *
+									 * This applies only if:
+									 * - the device was bonded before an upgrade,
+									 * - the Application or the Bootloader is upgraded (upgrade of the Soft Device will erase the bond information anyway),
+									 *     - Application:
+									  *        if the DFU Bootloader has been modified and compiled to preserve the LTK and the ATT table after application upgrade (at least 2 pages)
+									 *         See: \Nordic\nrf51\components\libraries\bootloader_dfu\dfu_types.h, line 56:
+									 *          #define DFU_APP_DATA_RESERVED           0x0000  ->  0x0800+   //< Size of Application Data that must be preserved between application updates...
+									 *     - Bootloader:
+									 *         The Application memory should not be removed when the Bootloader is upgraded, so the Bootloader configuration does not matter.
+									 *
+									 * If the bond information is not to be preserved between the old and new applications, we may skip this disconnect/reconnect process.
+									 * The DFU Bootloader will send the SD indication anyway when we will just continue here, as the information whether it should send it or not it is not being
+									 * read from the application's ATT table, but rather passed as an argument of the "reboot to bootloader" method.
+									 */
+									final boolean keepBond = intent.getBooleanExtra(EXTRA_KEEP_BOND, false);
+									if (keepBond && (fileType & TYPE_SOFT_DEVICE) == 0) {
+										sendLogBroadcast(LOG_LEVEL_VERBOSE, "Restarting service...");
+
+										updateProgressNotification(PROGRESS_DISCONNECTING);
+										sendLogBroadcast(LOG_LEVEL_VERBOSE, "Disconnecting...");
+										gatt.disconnect();
+										waitUntilDisconnected();
+										sendLogBroadcast(LOG_LEVEL_INFO, "Disconnected");
+
+										// Close the device
+										close(gatt);
+
+										logi("Restarting service");
+										final Intent newIntent = new Intent();
+										newIntent.fillIn(intent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_PACKAGE);
+										startService(newIntent);
+										return;
+									}
+								} else {
+									sendLogBroadcast(LOG_LEVEL_APPLICATION, "Service Changed indications enabled");
+								}
+								hasServiceChanged = true;
+							}
+						}
+					}
+
+					sendLogBroadcast(LOG_LEVEL_VERBOSE, "Jumping to the DFU Bootloader...");
+
+					// Enable notifications
+					enableCCCD(gatt, controlPointCharacteristic, NOTIFICATIONS);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Notifications enabled");
+
+					// Send 'jump to bootloader command' (Start DFU)
+					updateProgressNotification(PROGRESS_ENABLING_DFU_MODE);
+					OP_CODE_START_DFU[1] = 0x04;
+					logi("Sending Start DFU command (Op Code = 1, Upload Mode = 4)");
+					writeOpCode(gatt, controlPointCharacteristic, OP_CODE_START_DFU, true);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Jump to bootloader sent (Op Code = 1, Upload Mode = 4)");
+
+					// The device will reset so we don't have to send Disconnect signal.
+					waitUntilDisconnected();
+					sendLogBroadcast(LOG_LEVEL_INFO, "Disconnected by the remote device");
+
+					/*
+					 * We would like to avoid using the hack with refreshing the device (refresh method is not in the public API). The refresh method clears the cached services and causes a
+					 * service discovery afterwards (when connected). Android, however, does it itself when receive the Service Changed indication when bonded.
+					 * In case of unpaired device we may either refresh the services manually (using the hack), or include the Service Changed characteristic.
+					 *
+					 * According to Bluetooth Core 4.0 (and 4.1) specification:
+					 *
+					 * [Vol. 3, Part G, 2.5.2 - Attribute Caching]
+					 * Note: Clients without a trusted relationship must perform service discovery on each connection if the server supports the Services Changed characteristic.
+					 *
+					 * However, as up to Android 5 the system does NOT respect this requirement and servers are cached for every device, even if Service Changed is enabled -> Android BUG?
+					 * For bonded devices Android performs service re-discovery when SC indication is received.
+					 */
+					refreshDeviceCache(gatt, !hasServiceChanged);
+
+					// Close the device
+					close(gatt);
+
+					logi("Starting service that will connect to the DFU bootloader");
+					final Intent newIntent = new Intent();
+					newIntent.fillIn(intent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_PACKAGE);
+					startService(newIntent);
+					return;
+				}
+
+				/*
+				 * If the DFU Version characteristic is present and the version returned from it is greater or equal to 0.5, the Extended Init Packet is required.
+				 * If the InputStream with init packet is null we may safely abort sending and reset the device as it would happen eventually in few moments.
+				 * The DFU target would send DFU INVALID STATE error if the init packet would not be sent before starting file transmission.
+				 */
+				if (version >= 5 && initIs == null) {
+					logw("Init packet not set for the DFU Bootloader version " + version);
+					sendLogBroadcast(LOG_LEVEL_ERROR, "The Init packet is required by this version DFU Bootloader");
+					terminateConnection(gatt, ERROR_INIT_PACKET_REQUIRED);
+					return;
+				}
+
+				// Enable notifications
+				enableCCCD(gatt, controlPointCharacteristic, NOTIFICATIONS);
+				sendLogBroadcast(LOG_LEVEL_APPLICATION, "Notifications enabled");
+
+				try {
+					// Set up the temporary variable that will hold the responses
+					byte[] response;
+					int status;
+
+					/*
+					 * The first version of DFU supported only an Application update.
+					 * Initializing procedure:
+					 * [DFU Start (0x01)] -> DFU Control Point
+					 * [App size in bytes (UINT32)] -> DFU Packet
+					 * ---------------------------------------------------------------------
+					 * Since SDK 6.0 and Soft Device 7.0+ the DFU supports upgrading Soft Device, Bootloader and Application.
+					 * Initializing procedure:
+					 * [DFU Start (0x01), <Update Mode>] -> DFU Control Point
+					 * [SD size in bytes (UINT32), Bootloader size in bytes (UINT32), Application size in bytes (UINT32)] -> DFU Packet
+					 * where <Upload Mode> is a bit mask:
+					 * 0x01 - Soft Device update
+					 * 0x02 - Bootloader update
+					 * 0x04 - Application update
+					 * so that
+					 * 0x03 - Soft Device and Bootloader update
+					 * If <Upload Mode> equals 5, 6 or 7 DFU target may return OPERATION_NOT_SUPPORTED [10, 01, 03]. In that case service will try to send
+					 * Soft Device and/or Bootloader first, reconnect to the new Bootloader and send the Application in the second connection.
+					 * --------------------------------------------------------------------
+					 * If DFU target supports only the old DFU, a response [10, 01, 03] will be send as a notification on DFU Control Point characteristic, where:
+					 * 10 - Response for...
+					 * 01 - DFU Start command
+					 * 03 - Operation Not Supported
+					 * (see table below)
+					 * In that case:
+					 * 1. If this is application update - service will try to upload using the old DFU protocol.
+					 * 2. In case of SD or BL update an error is returned.
+					 */
+
+					// Obtain size of image(s)
+					int softDeviceImageSize = (fileType & TYPE_SOFT_DEVICE) > 0 ? imageSizeInBytes : 0;
+					int bootloaderImageSize = (fileType & TYPE_BOOTLOADER) > 0 ? imageSizeInBytes : 0;
+					int appImageSize = (fileType & TYPE_APPLICATION) > 0 ? imageSizeInBytes : 0;
+					// The sizes above may be overwritten if a ZIP file was passed
+					if (MIME_TYPE_ZIP.equals(mimeType)) {
+						final ArchiveInputStream zhis = (ArchiveInputStream) is;
+						softDeviceImageSize = zhis.softDeviceImageSize();
+						bootloaderImageSize = zhis.bootloaderImageSize();
+						appImageSize = zhis.applicationImageSize();
+					}
+
+					try {
+						OP_CODE_START_DFU[1] = (byte) fileType;
+
+						// Send Start DFU command to Control Point
+						logi("Sending Start DFU command (Op Code = 1, Upload Mode = " + fileType + ")");
+						writeOpCode(gatt, controlPointCharacteristic, OP_CODE_START_DFU);
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "DFU Start sent (Op Code = 1, Upload Mode = " + fileType + ")");
+
+						// Send image size in bytes to DFU Packet
+						logi("Sending image size array to DFU Packet (" + softDeviceImageSize + "b, " + bootloaderImageSize + "b, " + appImageSize + "b)");
+						writeImageSize(gatt, packetCharacteristic, softDeviceImageSize, bootloaderImageSize, appImageSize);
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Firmware image size sent (" + softDeviceImageSize + "b, " + bootloaderImageSize + "b, " + appImageSize + "b)");
+
+						// A notification will come with confirmation. Let's wait for it a bit
+						response = readNotificationResponse();
+
+						/*
+						 * The response received from the DFU device contains:
+						 * +---------+--------+----------------------------------------------------+
+						 * | byte no | value  | description                                        |
+						 * +---------+--------+----------------------------------------------------+
+						 * | 0       | 16     | Response code                                      |
+						 * | 1       | 1      | The Op Code of a request that this response is for |
+						 * | 2       | STATUS | See DFU_STATUS_* for status codes                  |
+						 * +---------+--------+----------------------------------------------------+
+						 */
+						status = getStatusCode(response, OP_CODE_START_DFU_KEY);
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Response received (Op Code = " + response[1] + " Status = " + status + ")");
+						if (status != DFU_STATUS_SUCCESS)
+							throw new RemoteDfuException("Starting DFU failed", status);
+					} catch (final RemoteDfuException e) {
+						try {
+							if (e.getErrorNumber() != DFU_STATUS_NOT_SUPPORTED)
+								throw e;
+
+							// If user wants to send the Soft Device and/or the Bootloader + Application we may try to send the Soft Device/Bootloader files first,
+							// and then reconnect and send the application in the second connection.
+							if ((fileType & TYPE_APPLICATION) > 0 && (fileType & (TYPE_SOFT_DEVICE | TYPE_BOOTLOADER)) > 0) {
+								// Clear the remote error flag
+								mRemoteErrorOccurred = false;
+
+								logw("DFU target does not support (SD/BL)+App update");
+								sendLogBroadcast(LOG_LEVEL_WARNING, "DFU target does not support (SD/BL)+App update");
+
+								fileType &= ~TYPE_APPLICATION; // clear application bit
+								mFileType = fileType;
+								OP_CODE_START_DFU[1] = (byte) fileType;
+								mPartsTotal = 2;
+
+								// Set new content type in the ZIP Input Stream and update sizes of images
+								final ArchiveInputStream zhis = (ArchiveInputStream) is;
+								zhis.setContentType(fileType);
+								try {
+									appImageSize = 0;
+									mImageSizeInBytes = is.available();
+								} catch (final IOException e1) {
+									// never happen
+								}
+
+								// Send Start DFU command to Control Point
+								sendLogBroadcast(LOG_LEVEL_VERBOSE, "Sending only SD/BL");
+								logi("Resending Start DFU command (Op Code = 1, Upload Mode = " + fileType + ")");
+								writeOpCode(gatt, controlPointCharacteristic, OP_CODE_START_DFU);
+								sendLogBroadcast(LOG_LEVEL_APPLICATION, "DFU Start sent (Op Code = 1, Upload Mode = " + fileType + ")");
+
+								// Send image size in bytes to DFU Packet
+								logi("Sending image size array to DFU Packet: [" + softDeviceImageSize + "b, " + bootloaderImageSize + "b, " + appImageSize + "b]");
+								writeImageSize(gatt, packetCharacteristic, softDeviceImageSize, bootloaderImageSize, appImageSize);
+								sendLogBroadcast(LOG_LEVEL_APPLICATION, "Firmware image size sent [" + softDeviceImageSize + "b, " + bootloaderImageSize + "b, " + appImageSize + "b]");
+
+								// A notification will come with confirmation. Let's wait for it a bit
+								response = readNotificationResponse();
+								status = getStatusCode(response, OP_CODE_START_DFU_KEY);
+								sendLogBroadcast(LOG_LEVEL_APPLICATION, "Response received (Op Code = " + response[1] + " Status = " + status + ")");
+								if (status != DFU_STATUS_SUCCESS)
+									throw new RemoteDfuException("Starting DFU failed", status);
+							} else
+								throw e;
+						} catch (final RemoteDfuException e1) {
+							if (e1.getErrorNumber() != DFU_STATUS_NOT_SUPPORTED)
+								throw e1;
+
+							// If operation is not supported by DFU target we may try to upload application with legacy mode, using the old DFU protocol
+							if (fileType == TYPE_APPLICATION) {
+								// Clear the remote error flag
+								mRemoteErrorOccurred = false;
+
+								// The DFU target does not support DFU v.2 protocol
+								logw("DFU target does not support DFU v.2");
+								sendLogBroadcast(LOG_LEVEL_WARNING, "DFU target does not support DFU v.2");
+
+								// Send Start DFU command to Control Point
+								sendLogBroadcast(LOG_LEVEL_VERBOSE, "Switching to DFU v.1");
+								logi("Resending Start DFU command (Op Code = 1)");
+								writeOpCode(gatt, controlPointCharacteristic, OP_CODE_START_DFU); // If has 2 bytes, but the second one is ignored
+								sendLogBroadcast(LOG_LEVEL_APPLICATION, "DFU Start sent (Op Code = 1)");
+
+								// Send image size in bytes to DFU Packet
+								logi("Sending application image size to DFU Packet: " + imageSizeInBytes + " bytes");
+								writeImageSize(gatt, packetCharacteristic, mImageSizeInBytes);
+								sendLogBroadcast(LOG_LEVEL_APPLICATION, "Firmware image size sent (" + imageSizeInBytes + " bytes)");
+
+								// A notification will come with confirmation. Let's wait for it a bit
+								response = readNotificationResponse();
+								status = getStatusCode(response, OP_CODE_START_DFU_KEY);
+								sendLogBroadcast(LOG_LEVEL_APPLICATION, "Response received (Op Code = " + response[1] + ", Status = " + status + ")");
+								if (status != DFU_STATUS_SUCCESS)
+									throw new RemoteDfuException("Starting DFU failed", status);
+							} else
+								throw e1;
+						}
+					}
+
+					// Since SDK 6.1 this delay is no longer required as the Receive Start DFU notification is postponed until the memory is clear.
+
+					//		if ((fileType & TYPE_SOFT_DEVICE) > 0) {
+					//			// In the experimental version of bootloader (SDK 6.0.0) we must wait some time until we can proceed with Soft Device update. Bootloader must prepare the RAM for the new firmware.
+					//			// Most likely this step will not be needed in the future as the notification received a moment before will be postponed until Bootloader is ready.
+					//			synchronized (this) {
+					//				try {
+					//					wait(6000);
+					//				} catch (final InterruptedException e) {
+					//					// do nothing
+					//				}
+					//			}
+					//		}
+
+					/*
+					 * If the DFU Version characteristic is present and the version returned from it is greater or equal to 0.5, the Extended Init Packet is required.
+					 * For older versions, or if the DFU Version characteristic is not present (pre SDK 7.0.0), the Init Packet (which could have contained only the firmware CRC) was optional.
+					 * Deprecated: To calculate the CRC (CRC-CCTII-16 0xFFFF) the following application may be used: http://www.lammertbies.nl/comm/software/index.html -> CRC library.
+					 * New: To calculate the CRC (CRC-CCTII-16 0xFFFF) the 'nrf utility' may be used (see below).
+					 *
+					 * The Init Packet is read from the *.dat file as a binary file. This service you allows to specify the init packet file in two ways.
+					 * Since SDK 8.0 and the DFU Library v0.6 using the Distribution packet (ZIP) is recommended. The distribution packet can be created using the
+					 * *nrf utility* tool, available together with Master Control Panel v 3.8.0+. See the DFU documentation at http://developer.nordicsemi.com for more details.
+					 * An init file may be also provided as a separate file using the {@link #EXTRA_INIT_FILE_PATH} or {@link #EXTRA_INIT_FILE_URI} or in the ZIP file
+					 * with the deprecated fixed naming convention:
+					 *
+					 *    a) If the ZIP file contain a softdevice.hex (or .bin) and/or bootloader.hex (or .bin) the 'system.dat' must also be included.
+					 *       In case when both files are present the CRC should be calculated from the two BIN contents merged together.
+					 *       This means: if there are softdevice.hex and bootloader.hex files in the ZIP file you have to convert them to BIN
+					 *       (e.g. using: http://hex2bin.sourceforge.net/ application), copy them into a single file where the soft device is placed as the first one and calculate
+					 *       the CRC for the whole file.
+					 *
+					 *    b) If the ZIP file contains a application.hex (or .bin) file the 'application.dat' file must be included and contain the Init packet for the application.
+					 */
+					// Send DFU Init Packet
+					if (initIs != null) {
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Writing Initialize DFU Parameters...");
+
+						logi("Sending the Initialize DFU Parameters START (Op Code = 2, Value = 0)");
+						writeOpCode(gatt, controlPointCharacteristic, OP_CODE_INIT_DFU_PARAMS_START);
+
+						try {
+							byte[] data = new byte[20];
+							int size;
+							while ((size = initIs.read(data, 0, data.length)) != -1) {
+								writeInitPacket(gatt, packetCharacteristic, data, size);
+							}
+						} catch (final IOException e) {
+							loge("Error while reading Init packet file");
+							throw new DfuException("Error while reading Init packet file", ERROR_FILE_ERROR);
+						}
+						logi("Sending the Initialize DFU Parameters COMPLETE (Op Code = 2, Value = 1)");
+						writeOpCode(gatt, controlPointCharacteristic, OP_CODE_INIT_DFU_PARAMS_COMPLETE);
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Initialize DFU Parameters completed");
+
+						// A notification will come with confirmation. Let's wait for it a bit
+						response = readNotificationResponse();
+						status = getStatusCode(response, OP_CODE_INIT_DFU_PARAMS_KEY);
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Response received (Op Code = " + response[1] + ", Status = " + status + ")");
+						if (status != DFU_STATUS_SUCCESS)
+							throw new RemoteDfuException("Device returned error after sending init packet", status);
+					} else
+						mInitPacketSent = true;
+
+					// Send the number of packets of firmware before receiving a receipt notification
+					final int numberOfPacketsBeforeNotification = mPacketsBeforeNotification;
+					if (numberOfPacketsBeforeNotification > 0) {
+						logi("Sending the number of packets before notifications (Op Code = 8, Value = " + numberOfPacketsBeforeNotification + ")");
+						setNumberOfPackets(OP_CODE_PACKET_RECEIPT_NOTIF_REQ, numberOfPacketsBeforeNotification);
+						writeOpCode(gatt, controlPointCharacteristic, OP_CODE_PACKET_RECEIPT_NOTIF_REQ);
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Packet Receipt Notif Req (Op Code = 8) sent (Value = " + numberOfPacketsBeforeNotification + ")");
+					}
+
+					// Initialize firmware upload
+					logi("Sending Receive Firmware Image request (Op Code = 3)");
+					writeOpCode(gatt, controlPointCharacteristic, OP_CODE_RECEIVE_FIRMWARE_IMAGE);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Receive Firmware Image request sent");
+
+					// Send the firmware. The method below sends the first packet and waits until the whole firmware is sent.
+					final long startTime = mLastProgressTime = mStartTime = SystemClock.elapsedRealtime();
+					updateProgressNotification();
+					try {
+						logi("Uploading firmware...");
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Uploading firmware...");
+						response = uploadFirmwareImage(gatt, packetCharacteristic, is);
+					} catch (final DeviceDisconnectedException e) {
+						loge("Disconnected while sending data");
+						throw e;
+						// TODO reconnect?
+					}
+					final long endTime = SystemClock.elapsedRealtime();
+
+					// Check the result of the operation
+					status = getStatusCode(response, OP_CODE_RECEIVE_FIRMWARE_IMAGE_KEY);
+					logi("Response received. Op Code: " + response[0] + " Req Op Code = " + response[1] + ", Status = " + response[2]);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Response received (Op Code = " + response[1] + ", Status = " + status + ")");
+					if (status != DFU_STATUS_SUCCESS)
+						throw new RemoteDfuException("Device returned error after sending file", status);
+
+					logi("Transfer of " + mBytesSent + " bytes has taken " + (endTime - startTime) + " ms");
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Upload completed in " + (endTime - startTime) + " ms");
+
+					// Send Validate request
+					logi("Sending Validate request (Op Code = 4)");
+					writeOpCode(gatt, controlPointCharacteristic, OP_CODE_VALIDATE);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Validate request sent");
+
+					// A notification will come with status code. Let's wait for it a bit.
+					response = readNotificationResponse();
+					status = getStatusCode(response, OP_CODE_VALIDATE_KEY);
+					logi("Response received. Op Code: " + response[0] + " Req Op Code = " + response[1] + ", Status = " + response[2]);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Response received (Op Code = " + response[1] + ", Status = " + status + ")");
+					if (status != DFU_STATUS_SUCCESS)
+						throw new RemoteDfuException("Device returned validation error", status);
+
+					// Send Activate and Reset signal.
+					updateProgressNotification(PROGRESS_DISCONNECTING);
+					logi("Sending Activate and Reset request (Op Code = 5)");
+					writeOpCode(gatt, controlPointCharacteristic, OP_CODE_ACTIVATE_AND_RESET);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Activate and Reset request sent");
+
+					// The device will reset so we don't have to send Disconnect signal.
+					waitUntilDisconnected();
+					sendLogBroadcast(LOG_LEVEL_INFO, "Disconnected by the remote device");
+
+					// In the DFU version 0.5, in case the device is bonded, the target device does not send the Service Changed indication after
+					// a jump from bootloader mode to app mode. This issue has been fixed in DFU version 0.6 (SDK 8.0). If the DFU bootloader has been
+					// configured to preserve the bond information we do not need to enforce refreshing services, as it will notify the phone using the
+					// Service Changed indication.
+					final boolean keepBond = intent.getBooleanExtra(EXTRA_KEEP_BOND, false);
+					refreshDeviceCache(gatt, version == 5 || !keepBond);
+
+					// Close the device
+					close(gatt);
+
+					// During the update the bonding information on the target device may have been removed.
+					// To create bond with the new application set the EXTRA_RESTORE_BOND extra to true.
+					// In case the bond information is copied to the new application the new bonding is not required.
+					if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_BONDED) {
+						final boolean restoreBond = intent.getBooleanExtra(EXTRA_RESTORE_BOND, false);
+
+						if (restoreBond || !keepBond || (fileType & TYPE_SOFT_DEVICE) > 0) {
+							// The bond information was lost.
+							removeBond(gatt.getDevice());
+
+							// Give some time for removing the bond information. 300ms was to short, let's set it to 2 seconds just to be sure.
+							synchronized (this) {
+								try {
+									wait(2000);
+								} catch (InterruptedException e) {
+									// do nothing
+								}
+							}
+						}
+
+						if (restoreBond && (fileType & TYPE_APPLICATION) > 0) {
+							// Restore pairing when application was updated.
+							createBond(gatt.getDevice());
+						}
+					}
+
+					/*
+					 * We need to send PROGRESS_COMPLETED message only when all files has been transmitted.
+					 * In case you want to send the Soft Device and/or Bootloader and the Application, the service will be started twice: one to send SD+BL, and the
+					 * second time to send the Application only (using the new Bootloader). In the first case we do not send PROGRESS_COMPLETED notification.
+					 */
+					if (mPartCurrent == mPartsTotal) {
+						// Delay this event a little bit. Android needs some time to prepare for reconnection.
+						synchronized (mLock) {
+							try {
+								mLock.wait(1400);
+							} catch (final InterruptedException e) {
+								// do nothing
+							}
+						}
+						updateProgressNotification(PROGRESS_COMPLETED);
+					} else {
+						/*
+						 * In case when the Soft Device has been upgraded, and the application should be send in the following connection, we have to
+						 * make sure that we know the address the device is advertising with. Depending on the method used to start the DFU bootloader the first time
+						 * the new Bootloader may advertise with the same address or one incremented by 1.
+						 * When the buttonless update was used, the bootloader will use the same address as the application. The cached list of services on the Android device
+						 * should be cleared thanks to the Service Changed characteristic (the fact that it exists if not bonded, or the Service Changed indication on bonded one).
+						 * In case of forced DFU mode (using a button), the Bootloader does not know whether there was the Service Changed characteristic present in the list of
+						 * application's services so it must advertise with a different address. The same situation applies when the new Soft Device was uploaded and the old
+						 * application has been removed in this process.
+						 *
+						 * We could have save the fact of jumping as a parameter of the service but it ma be that some Android devices must first scan a device before connecting to it.
+						 * It a device with the address+1 has never been detected before the service could have failed on connection.
+						 */
+						sendLogBroadcast(LOG_LEVEL_VERBOSE, "Scanning for the DFU Bootloader...");
+						final String newAddress = BootloaderScannerFactory.getScanner().searchFor(mDeviceAddress);
+						if (newAddress != null)
+							sendLogBroadcast(LOG_LEVEL_INFO, "DFU Bootloader found with address " + newAddress);
+						else {
+							sendLogBroadcast(LOG_LEVEL_INFO, "DFU Bootloader not found. Trying the same address...");
+						}
+
+						/*
+						 * The current service instance has uploaded the Soft Device and/or Bootloader.
+						 * We need to start another instance that will try to send application only.
+						 */
+						logi("Starting service that will upload application");
+						final Intent newIntent = new Intent();
+						newIntent.fillIn(intent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_PACKAGE);
+						newIntent.putExtra(EXTRA_FILE_MIME_TYPE, MIME_TYPE_ZIP); // ensure this is set (e.g. for scripts)
+						newIntent.putExtra(EXTRA_FILE_TYPE, TYPE_APPLICATION); // set the type to application only
+						if (newAddress != null)
+							newIntent.putExtra(EXTRA_DEVICE_ADDRESS, newAddress);
+						newIntent.putExtra(EXTRA_PART_CURRENT, mPartCurrent + 1);
+						newIntent.putExtra(EXTRA_PARTS_TOTAL, mPartsTotal);
+						startService(newIntent);
+					}
+				} catch (final UnknownResponseException e) {
+					final int error = ERROR_INVALID_RESPONSE;
+					loge(e.getMessage());
+					sendLogBroadcast(LOG_LEVEL_ERROR, e.getMessage());
+
+					logi("Sending Reset command (Op Code = 6)");
+					writeOpCode(gatt, controlPointCharacteristic, OP_CODE_RESET);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Reset request sent");
+					terminateConnection(gatt, error);
+				} catch (final RemoteDfuException e) {
+					final int error = ERROR_REMOTE_MASK | e.getErrorNumber();
+					loge(e.getMessage());
+					sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Remote DFU error: %s", GattError.parse(error)));
+
+					logi("Sending Reset command (Op Code = 6)");
+					writeOpCode(gatt, controlPointCharacteristic, OP_CODE_RESET);
+					sendLogBroadcast(LOG_LEVEL_APPLICATION, "Reset request sent");
+					terminateConnection(gatt, error);
+				}
+			} catch (final UploadAbortedException e) {
+				logi("Upload aborted");
+				sendLogBroadcast(LOG_LEVEL_WARNING, "Upload aborted");
+				if (mConnectionState == STATE_CONNECTED_AND_READY)
+					try {
+						mAborted = false;
+						logi("Sending Reset command (Op Code = 6)");
+						writeOpCode(gatt, controlPointCharacteristic, OP_CODE_RESET);
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Reset request sent");
+					} catch (final Exception e1) {
+						// do nothing
+					}
+				terminateConnection(gatt, PROGRESS_ABORTED);
+			} catch (final DeviceDisconnectedException e) {
+				sendLogBroadcast(LOG_LEVEL_ERROR, "Device has disconnected");
+				// TODO reconnect n times?
+				loge(e.getMessage());
+				close(gatt);
+				updateProgressNotification(ERROR_DEVICE_DISCONNECTED);
+			} catch (final DfuException e) {
+				int error = e.getErrorNumber();
+				// Connection state errors and other Bluetooth GATT callbacks share the same error numbers. Therefore we are using bit masks to identify the type.
+				if ((error & ERROR_CONNECTION_STATE_MASK) > 0) {
+					error &= ~ERROR_CONNECTION_STATE_MASK;
+					sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Error (0x%02X): %s", error, GattError.parseConnectionError(error)));
+				} else {
+					error &= ~ERROR_CONNECTION_MASK;
+					sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Error (0x%02X): %s", error, GattError.parse(error)));
+				}
+				loge(e.getMessage());
+				if (mConnectionState == STATE_CONNECTED_AND_READY)
+					try {
+						logi("Sending Reset command (Op Code = 6)");
+						writeOpCode(gatt, controlPointCharacteristic, OP_CODE_RESET);
+						sendLogBroadcast(LOG_LEVEL_APPLICATION, "Reset request sent");
+					} catch (final Exception e1) {
+						// do nothing
+					}
+				terminateConnection(gatt, e.getErrorNumber() /* we return the whole error number, including the error type mask */);
+			}
+		} finally {
+			try {
+				// Ensure that input stream is always closed
+				mInputStream = null;
+				if (is != null)
+					is.close();
+			} catch (final IOException e) {
+				// do nothing
+			}
+		}
+	}
+
+	/**
+	 * Sets number of data packets that will be send before the notification will be received.
+	 *
+	 * @param data  control point data packet
+	 * @param value number of packets before receiving notification. If this value is 0, then the notification of packet receipt will be disabled by the DFU target.
+	 */
+	private void setNumberOfPackets(final byte[] data, final int value) {
+		data[1] = (byte) (value & 0xFF);
+		data[2] = (byte) ((value >> 8) & 0xFF);
+	}
+
+	/**
+	 * Opens the binary input stream that returns the firmware image content. A Path to the file is given.
+	 *
+	 * @param filePath the path to the HEX, BIN or ZIP file
+	 * @param mimeType the file type
+	 * @param mbrSize  the size of MBR, by default 0x1000
+	 * @param types    the content files types in ZIP
+	 * @return the input stream with binary image content
+	 */
+	private InputStream openInputStream(final String filePath, final String mimeType, final int mbrSize, final int types) throws IOException {
+		final InputStream is = new FileInputStream(filePath);
+		if (MIME_TYPE_ZIP.equals(mimeType))
+			return new ArchiveInputStream(is, mbrSize, types);
+		if (filePath.toLowerCase(Locale.US).endsWith("hex"))
+			return new HexInputStream(is, mbrSize);
+		return is;
+	}
+
+	/**
+	 * Opens the binary input stream. A Uri to the stream is given.
+	 *
+	 * @param stream   the Uri to the stream
+	 * @param mimeType the file type
+	 * @param mbrSize  the size of MBR, by default 0x1000
+	 * @param types    the content files types in ZIP
+	 * @return the input stream with binary image content
+	 */
+	private InputStream openInputStream(final Uri stream, final String mimeType, final int mbrSize, final int types) throws IOException {
+		final InputStream is = getContentResolver().openInputStream(stream);
+		if (MIME_TYPE_ZIP.equals(mimeType))
+			return new ArchiveInputStream(is, mbrSize, types);
+
+		final String[] projection = {MediaStore.Images.Media.DISPLAY_NAME};
+		final Cursor cursor = getContentResolver().query(stream, projection, null, null, null);
+		try {
+			if (cursor.moveToNext()) {
+				final String fileName = cursor.getString(0 /* DISPLAY_NAME*/);
+
+				if (fileName.toLowerCase(Locale.US).endsWith("hex"))
+					return new HexInputStream(is, mbrSize);
+			}
+		} finally {
+			cursor.close();
+		}
+		return is;
+	}
+
+	/**
+	 * Opens the binary input stream that returns the firmware image content. A resource id in the res/raw is given.
+	 *
+	 * @param resId the if of the resource file
+	 * @param mimeType the file type
+	 * @param mbrSize  the size of MBR, by default 0x1000
+	 * @param types    the content files types in ZIP
+	 * @return the input stream with binary image content
+	 */
+	private InputStream openInputStream(final int resId, final String mimeType, final int mbrSize, final int types) throws IOException {
+		final InputStream is = getResources().openRawResource(resId);
+		if (MIME_TYPE_ZIP.equals(mimeType))
+			return new ArchiveInputStream(is, mbrSize, types);
+		is.mark(2);
+		int firstByte = is.read();
+		is.reset();
+		if (firstByte == ':')
+			return new HexInputStream(is, mbrSize);
+		return is;
+	}
+
+	/**
+	 * Connects to the BLE device with given address. This method is SYNCHRONOUS, it wait until the connection status change from {@link #STATE_CONNECTING} to {@link #STATE_CONNECTED_AND_READY} or an
+	 * error occurs. This method returns <code>null</code> if Bluetooth adapter is disabled.
+	 *
+	 * @param address the device address
+	 * @return the GATT device or <code>null</code> if Bluetooth adapter is disabled.
+	 */
+	private BluetoothGatt connect(final String address) {
+		if (!mBluetoothAdapter.isEnabled())
+			return null;
+
+		mConnectionState = STATE_CONNECTING;
+
+		logi("Connecting to the device...");
+		final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+		final BluetoothGatt gatt = device.connectGatt(this, false, mGattCallback);
+
+		// We have to wait until the device is connected and services are discovered
+		// Connection error may occur as well.
+		try {
+			synchronized (mLock) {
+				while (((mConnectionState == STATE_CONNECTING || mConnectionState == STATE_CONNECTED) && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		return gatt;
+	}
+
+	/**
+	 * Disconnects from the device and cleans local variables in case of error. This method is SYNCHRONOUS and wait until the disconnecting process will be completed.
+	 *
+	 * @param gatt  the GATT device to be disconnected
+	 * @param error error number
+	 */
+	private void terminateConnection(final BluetoothGatt gatt, final int error) {
+		if (mConnectionState != STATE_DISCONNECTED) {
+			updateProgressNotification(PROGRESS_DISCONNECTING);
+
+			// No need to disable notifications
+
+			// Disconnect from the device
+			disconnect(gatt);
+			sendLogBroadcast(LOG_LEVEL_INFO, "Disconnected");
+		}
+
+		// Close the device
+		refreshDeviceCache(gatt, false); // This should be set to true when DFU Version is 0.5 or lower
+		close(gatt);
+		updateProgressNotification(error);
+	}
+
+	/**
+	 * Disconnects from the device. This is SYNCHRONOUS method and waits until the callback returns new state. Terminates immediately if device is already disconnected. Do not call this method
+	 * directly, use {@link #terminateConnection(android.bluetooth.BluetoothGatt, int)} instead.
+	 *
+	 * @param gatt the GATT device that has to be disconnected
+	 */
+	private void disconnect(final BluetoothGatt gatt) {
+		if (mConnectionState == STATE_DISCONNECTED)
+			return;
+
+		mConnectionState = STATE_DISCONNECTING;
+
+		logi("Disconnecting from the device...");
+		gatt.disconnect();
+
+		// We have to wait until device gets disconnected or an error occur
+		waitUntilDisconnected();
+	}
+
+	/**
+	 * Wait until the connection state will change to {@link #STATE_DISCONNECTED} or until an error occurs.
+	 */
+	private void waitUntilDisconnected() {
+		try {
+			synchronized (mLock) {
+				while (mConnectionState != STATE_DISCONNECTED && mError == 0)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+	}
+
+	/**
+	 * Closes the GATT device and cleans up.
+	 *
+	 * @param gatt the GATT device to be closed
+	 */
+	private void close(final BluetoothGatt gatt) {
+		logi("Cleaning up...");
+		sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.close()");
+		gatt.close();
+		mConnectionState = STATE_CLOSED;
+	}
+
+	/**
+	 * Clears the device cache. After uploading new firmware the DFU target will have other services than before.
+	 *
+	 * @param gatt  the GATT device to be refreshed
+	 * @param force <code>true</code> to force the refresh
+	 */
+	private void refreshDeviceCache(final BluetoothGatt gatt, final boolean force) {
+		/*
+		 * If the device is bonded this is up to the Service Changed characteristic to notify Android that the services has changed.
+		 * There is no need for this trick in that case.
+		 * If not bonded, the Android should not keep the services cached when the Service Changed characteristic is present in the target device database.
+		 * However, due to the Android bug (still exists in Android 5.0.1), it is keeping them anyway and the only way to clear services is by using this hidden refresh method.
+		 */
+		if (force || gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
+			sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.refresh()");
+			/*
+			 * There is a refresh() method in BluetoothGatt class but for now it's hidden. We will call it using reflections.
+			 */
+			try {
+				final Method refresh = gatt.getClass().getMethod("refresh");
+				if (refresh != null) {
+					final boolean success = (Boolean) refresh.invoke(gatt);
+					logi("Refreshing result: " + success);
+				}
+			} catch (Exception e) {
+				loge("An exception occurred while refreshing device", e);
+				sendLogBroadcast(LOG_LEVEL_WARNING, "Refreshing failed");
+			}
+		}
+	}
+
+	/**
+	 * Checks whether the response received is valid and returns the status code.
+	 *
+	 * @param response the response received from the DFU device.
+	 * @param request  the expected Op Code
+	 * @return the status code
+	 * @throws UnknownResponseException if response was not valid
+	 */
+	private int getStatusCode(final byte[] response, final int request) throws UnknownResponseException {
+		if (response == null || response.length != 3 || response[0] != OP_CODE_RESPONSE_CODE_KEY || response[1] != request || response[2] < 1 || response[2] > 6)
+			throw new UnknownResponseException("Invalid response received", response, request);
+		return response[2];
+	}
+
+	/**
+	 * Reads the DFU Version characteristic if such exists. Otherwise it returns 0.
+	 *
+	 * @param gatt           the GATT device
+	 * @param characteristic the characteristic to read
+	 * @return a version number or 0 if not present on the bootloader
+	 * @throws DeviceDisconnectedException
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private int readVersion(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) throws DeviceDisconnectedException, DfuException, UploadAbortedException {
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to read version number", mConnectionState);
+		// If the DFU Version characteristic is not available we return 0.
+		if (characteristic == null)
+			return 0;
+
+		mReceivedData = null;
+		mError = 0;
+
+		logi("Reading DFU version number...");
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Reading DFU version number...");
+
+		gatt.readCharacteristic(characteristic);
+
+		// We have to wait until device receives a response or an error occur
+		try {
+			synchronized (mLock) {
+				while ((!mRequestCompleted && mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (mError != 0)
+			throw new DfuException("Unable to read version number", mError);
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to read version number", mConnectionState);
+
+		// The version is a 16-bit unsigned int
+		return characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0);
+	}
+
+	/**
+	 * Enables or disables the notifications for given characteristic. This method is SYNCHRONOUS and wait until the
+	 * {@link android.bluetooth.BluetoothGattCallback#onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int)} will be called or the connection state will change from {@link #STATE_CONNECTED_AND_READY}. If
+	 * connection state will change, or an error will occur, an exception will be thrown.
+	 *
+	 * @param gatt           the GATT device
+	 * @param characteristic the characteristic to enable or disable notifications for
+	 * @param type           {@link #NOTIFICATIONS} or {@link #INDICATIONS}
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private void enableCCCD(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int type) throws DeviceDisconnectedException, DfuException, UploadAbortedException {
+		final String debugString = type == NOTIFICATIONS ? "notifications" : "indications";
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to set " + debugString + " state", mConnectionState);
+
+		mReceivedData = null;
+		mError = 0;
+		if ((type == NOTIFICATIONS && mNotificationsEnabled) || (type == INDICATIONS && mServiceChangedIndicationsEnabled))
+			return;
+
+		logi("Enabling " + debugString + "...");
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Enabling " + debugString + " for " + characteristic.getUuid());
+
+		// enable notifications locally
+		gatt.setCharacteristicNotification(characteristic, true);
+
+		// enable notifications on the device
+		final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
+		descriptor.setValue(type == NOTIFICATIONS ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
+		sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.writeDescriptor(" + descriptor.getUuid() + (type == NOTIFICATIONS ? ", value=0x01-00)" : ", value=0x02-00)"));
+		gatt.writeDescriptor(descriptor);
+
+		// We have to wait until device receives a response or an error occur
+		try {
+			synchronized (mLock) {
+				while ((((type == NOTIFICATIONS && !mNotificationsEnabled) || (type == INDICATIONS && !mServiceChangedIndicationsEnabled))
+						&& mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (mError != 0)
+			throw new DfuException("Unable to set " + debugString + " state", mError);
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to set " + debugString + " state", mConnectionState);
+	}
+
+	/**
+	 * Reads the value of the Service Changed Client Characteristic Configuration descriptor (CCCD).
+	 *
+	 * @param gatt           the GATT device
+	 * @param characteristic the Service Changed characteristic
+	 * @return <code>true</code> if Service Changed CCCD is enabled ans set to INDICATE
+	 * @throws DeviceDisconnectedException
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private boolean isServiceChangedCCCDEnabled(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) throws DeviceDisconnectedException, DfuException, UploadAbortedException {
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to read Service Changed CCCD", mConnectionState);
+		// If the Service Changed characteristic or the CCCD is not available we return false.
+		if (characteristic == null)
+			return false;
+
+		final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
+		if (descriptor == null)
+			return false;
+
+		mRequestCompleted = false;
+		mError = 0;
+
+		logi("Reading Service Changed CCCD value...");
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Reading Service Changed CCCD value...");
+
+		gatt.readDescriptor(descriptor);
+
+		// We have to wait until device receives a response or an error occur
+		try {
+			synchronized (mLock) {
+				while ((!mRequestCompleted && mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (mError != 0)
+			throw new DfuException("Unable to read Service Changed CCCD", mError);
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to read Service Changed CCCD", mConnectionState);
+
+		return mServiceChangedIndicationsEnabled;
+	}
+
+	/**
+	 * Writes the operation code to the characteristic. This method is SYNCHRONOUS and wait until the
+	 * {@link android.bluetooth.BluetoothGattCallback#onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} will be called or the connection state will change from {@link #STATE_CONNECTED_AND_READY}.
+	 * If connection state will change, or an error will occur, an exception will be thrown.
+	 *
+	 * @param gatt           the GATT device
+	 * @param characteristic the characteristic to write to. Should be the DFU CONTROL POINT
+	 * @param value          the value to write to the characteristic
+	 * @throws DeviceDisconnectedException
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private void writeOpCode(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final byte[] value) throws DeviceDisconnectedException, DfuException, UploadAbortedException {
+		final boolean reset = value[0] == OP_CODE_RESET_KEY || value[0] == OP_CODE_ACTIVATE_AND_RESET_KEY;
+		writeOpCode(gatt, characteristic, value, reset);
+	}
+
+	/**
+	 * Writes the operation code to the characteristic. This method is SYNCHRONOUS and wait until the
+	 * {@link android.bluetooth.BluetoothGattCallback#onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} will be called or the connection state will change from {@link #STATE_CONNECTED_AND_READY}.
+	 * If connection state will change, or an error will occur, an exception will be thrown.
+	 *
+	 * @param gatt           the GATT device
+	 * @param characteristic the characteristic to write to. Should be the DFU CONTROL POINT
+	 * @param value          the value to write to the characteristic
+	 * @param reset          whether the command trigger restarting the device
+	 * @throws DeviceDisconnectedException
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private void writeOpCode(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final byte[] value, final boolean reset) throws DeviceDisconnectedException, DfuException,
+			UploadAbortedException {
+		mReceivedData = null;
+		mError = 0;
+		mRequestCompleted = false;
+		/*
+		 * Sending a command that will make the DFU target to reboot may cause an error 133 (0x85 - Gatt Error). If so, with this flag set, the error will not be shown to the user
+		 * as the peripheral is disconnected anyway. See: mGattCallback#onCharacteristicWrite(...) method
+		 */
+		mResetRequestSent = reset;
+
+		characteristic.setValue(value);
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Writing to characteristic " + characteristic.getUuid());
+		sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.writeCharacteristic(" + characteristic.getUuid() + ")");
+		gatt.writeCharacteristic(characteristic);
+
+		// We have to wait for confirmation
+		try {
+			synchronized (mLock) {
+				while ((!mRequestCompleted && mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (!mResetRequestSent && mError != 0)
+			throw new DfuException("Unable to write Op Code " + value[0], mError);
+		if (!mResetRequestSent && mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to write Op Code " + value[0], mConnectionState);
+	}
+
+	/**
+	 * Writes the image size to the characteristic. This method is SYNCHRONOUS and wait until the {@link android.bluetooth.BluetoothGattCallback#onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
+	 * will be called or the connection state will change from {@link #STATE_CONNECTED_AND_READY}. If connection state will change, or an error will occur, an exception will be thrown.
+	 *
+	 * @param gatt           the GATT device
+	 * @param characteristic the characteristic to write to. Should be the DFU PACKET
+	 * @param imageSize      the image size in bytes
+	 * @throws DeviceDisconnectedException
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private void writeImageSize(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int imageSize) throws DeviceDisconnectedException, DfuException,
+			UploadAbortedException {
+		mReceivedData = null;
+		mError = 0;
+		mImageSizeSent = false;
+
+		characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
+		characteristic.setValue(new byte[4]);
+		characteristic.setValue(imageSize, BluetoothGattCharacteristic.FORMAT_UINT32, 0);
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Writing to characteristic " + characteristic.getUuid());
+		sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.writeCharacteristic(" + characteristic.getUuid() + ")");
+		gatt.writeCharacteristic(characteristic);
+
+		// We have to wait for confirmation
+		try {
+			synchronized (mLock) {
+				while ((!mImageSizeSent && mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (mError != 0)
+			throw new DfuException("Unable to write Image Size", mError);
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to write Image Size", mConnectionState);
+	}
+
+	/**
+	 * <p>
+	 * Writes the Soft Device, Bootloader and Application image sizes to the characteristic. Soft Device and Bootloader update is supported since Soft Device s110 v7.0.0.
+	 * Sizes of SD, BL and App are uploaded as 3x UINT32 even though some of them may be 0s. F.e. if only App is being updated the data will be <0x00000000, 0x00000000, [App size]>
+	 * </p>
+	 * <p>
+	 * This method is SYNCHRONOUS and wait until the {@link android.bluetooth.BluetoothGattCallback#onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} will be called or the connection state will
+	 * change from {@link #STATE_CONNECTED_AND_READY}. If connection state will change, or an error will occur, an exception will be thrown.
+	 * </p>
+	 *
+	 * @param gatt                the GATT device
+	 * @param characteristic      the characteristic to write to. Should be the DFU PACKET
+	 * @param softDeviceImageSize the Soft Device image size in bytes
+	 * @param bootloaderImageSize the Bootloader image size in bytes
+	 * @param appImageSize        the Application image size in bytes
+	 * @throws DeviceDisconnectedException
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private void writeImageSize(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int softDeviceImageSize, final int bootloaderImageSize, final int appImageSize)
+			throws DeviceDisconnectedException, DfuException, UploadAbortedException {
+		mReceivedData = null;
+		mError = 0;
+		mImageSizeSent = false;
+
+		characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
+		characteristic.setValue(new byte[12]);
+		characteristic.setValue(softDeviceImageSize, BluetoothGattCharacteristic.FORMAT_UINT32, 0);
+		characteristic.setValue(bootloaderImageSize, BluetoothGattCharacteristic.FORMAT_UINT32, 4);
+		characteristic.setValue(appImageSize, BluetoothGattCharacteristic.FORMAT_UINT32, 8);
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Writing to characteristic " + characteristic.getUuid());
+		sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.writeCharacteristic(" + characteristic.getUuid() + ")");
+		gatt.writeCharacteristic(characteristic);
+
+		// We have to wait for confirmation
+		try {
+			synchronized (mLock) {
+				while ((!mImageSizeSent && mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (mError != 0)
+			throw new DfuException("Unable to write Image Sizes", mError);
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to write Image Sizes", mConnectionState);
+	}
+
+	/**
+	 * Writes the Init packet to the characteristic. This method is SYNCHRONOUS and wait until the {@link android.bluetooth.BluetoothGattCallback#onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
+	 * will be called or the connection state will change from {@link #STATE_CONNECTED_AND_READY}. If connection state will change, or an error will occur, an exception will be thrown.
+	 *
+	 * @param gatt           the GATT device
+	 * @param characteristic the characteristic to write to. Should be the DFU PACKET
+	 * @param buffer         the init packet as a byte array. This must be shorter or equal to 20 bytes (TODO check this restriction).
+	 * @param size           the init packet size
+	 * @throws DeviceDisconnectedException
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private void writeInitPacket(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final byte[] buffer, final int size) throws DeviceDisconnectedException, DfuException,
+			UploadAbortedException {
+		byte[] locBuffer = buffer;
+		if (buffer.length != size) {
+			locBuffer = new byte[size];
+			System.arraycopy(buffer, 0, locBuffer, 0, size);
+		}
+		mReceivedData = null;
+		mError = 0;
+		mInitPacketSent = false;
+
+		characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
+		characteristic.setValue(locBuffer);
+		logi("Sending init packet (Value = " + parse(locBuffer) + ")");
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Writing to characteristic " + characteristic.getUuid());
+		sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.writeCharacteristic(" + characteristic.getUuid() + ")");
+		gatt.writeCharacteristic(characteristic);
+
+		// We have to wait for confirmation
+		try {
+			synchronized (mLock) {
+				while ((!mInitPacketSent && mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (mError != 0)
+			throw new DfuException("Unable to write Init DFU Parameters", mError);
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to write Init DFU Parameters", mConnectionState);
+	}
+
+	/**
+	 * Starts sending the data. This method is SYNCHRONOUS and terminates when the whole file will be uploaded or the connection status will change from {@link #STATE_CONNECTED_AND_READY}. If
+	 * connection state will change, or an error will occur, an exception will be thrown.
+	 *
+	 * @param gatt                 the GATT device (DFU target)
+	 * @param packetCharacteristic the characteristic to write file content to. Must be the DFU PACKET
+	 * @return The response value received from notification with Op Code = 3 when all bytes will be uploaded successfully.
+	 * @throws DeviceDisconnectedException Thrown when the device will disconnect in the middle of the transmission. The error core will be saved in {@link #mConnectionState}.
+	 * @throws DfuException                Thrown if DFU error occur
+	 * @throws UploadAbortedException
+	 */
+	private byte[] uploadFirmwareImage(final BluetoothGatt gatt, final BluetoothGattCharacteristic packetCharacteristic, final InputStream inputStream) throws DeviceDisconnectedException,
+			DfuException, UploadAbortedException {
+		mReceivedData = null;
+		mError = 0;
+
+		final byte[] buffer = mBuffer;
+		try {
+			final int size = inputStream.read(buffer);
+			sendLogBroadcast(LOG_LEVEL_VERBOSE, "Sending firmware to characteristic " + packetCharacteristic.getUuid() + "...");
+			writePacket(gatt, packetCharacteristic, buffer, size);
+		} catch (final HexFileValidationException e) {
+			throw new DfuException("HEX file not valid", ERROR_FILE_INVALID);
+		} catch (final IOException e) {
+			throw new DfuException("Error while reading file", ERROR_FILE_IO_EXCEPTION);
+		}
+
+		try {
+			synchronized (mLock) {
+				while ((mReceivedData == null && mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (mError != 0)
+			throw new DfuException("Uploading Firmware Image failed", mError);
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Uploading Firmware Image failed: device disconnected", mConnectionState);
+
+		return mReceivedData;
+	}
+
+	/**
+	 * Writes the buffer to the characteristic. The maximum size of the buffer is 20 bytes. This method is ASYNCHRONOUS and returns immediately after adding the data to TX queue.
+	 *
+	 * @param gatt           the GATT device
+	 * @param characteristic the characteristic to write to. Should be the DFU PACKET
+	 * @param buffer         the buffer with 1-20 bytes
+	 * @param size           the number of bytes from the buffer to send
+	 */
+	private void writePacket(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final byte[] buffer, final int size) {
+		byte[] locBuffer = buffer;
+		if (buffer.length != size) {
+			locBuffer = new byte[size];
+			System.arraycopy(buffer, 0, locBuffer, 0, size);
+		}
+		characteristic.setValue(locBuffer);
+		gatt.writeCharacteristic(characteristic);
+		// FIXME BLE buffer overflow
+		// after writing to the device with WRITE_NO_RESPONSE property the onCharacteristicWrite callback is received immediately after writing data to a buffer.
+		// The real sending is much slower than adding to the buffer. This method does not return false if writing didn't succeed.. just the callback is not invoked.
+		//
+		// More info: this works fine on Nexus 5 (Android 4.4) (4.3 seconds) and on Samsung S4 (Android 4.3) (20 seconds) so this is a driver issue.
+		// Nexus 4 and 7 uses Qualcomm chip, Nexus 5 and Samsung uses Broadcom chips.
+	}
+
+	private void waitIfPaused() {
+		synchronized (mLock) {
+			try {
+				while (mPaused)
+					mLock.wait();
+			} catch (final InterruptedException e) {
+				loge("Sleeping interrupted", e);
+			}
+		}
+	}
+
+	@SuppressLint("NewApi")
+	private boolean createBond(final BluetoothDevice device) {
+		if (device.getBondState() == BluetoothDevice.BOND_BONDED)
+			return true;
+
+		boolean result;
+		mRequestCompleted = false;
+
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Starting pairing...");
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+			sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.getDevice().createBond()");
+			result = device.createBond();
+		} else {
+			result = createBondApi18(device);
+		}
+
+		// We have to wait until device is bounded
+		try {
+			synchronized (mLock) {
+				while (!mRequestCompleted && !mAborted)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		return result;
+	}
+
+	private boolean createBondApi18(final BluetoothDevice device) {
+		/*
+		 * There is a createBond() method in BluetoothDevice class but for now it's hidden. We will call it using reflections. It has been revealed in KitKat (Api19)
+		 */
+		try {
+			final Method createBond = device.getClass().getMethod("createBond");
+			if (createBond != null) {
+				sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.getDevice().createBond() (hidden)");
+				return (Boolean) createBond.invoke(device);
+			}
+		} catch (final Exception e) {
+			Log.w(TAG, "An exception occurred while creating bond", e);
+		}
+		return false;
+	}
+
+	/**
+	 * Removes the bond information for the given device.
+	 *
+	 * @param device the device to unbound
+	 * @return <code>true</code> if operation succeeded, <code>false</code> otherwise
+	 */
+	private boolean removeBond(final BluetoothDevice device) {
+		if (device.getBondState() == BluetoothDevice.BOND_NONE)
+			return true;
+
+		sendLogBroadcast(LOG_LEVEL_VERBOSE, "Removing bond information...");
+		boolean result = false;
+		/*
+		 * There is a removeBond() method in BluetoothDevice class but for now it's hidden. We will call it using reflections.
+		 */
+		try {
+			final Method removeBond = device.getClass().getMethod("removeBond");
+			if (removeBond != null) {
+				mRequestCompleted = false;
+				sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.getDevice().removeBond() (hidden)");
+				result = (Boolean) removeBond.invoke(device);
+
+				// We have to wait until device is unbounded
+				try {
+					synchronized (mLock) {
+						while (!mRequestCompleted && !mAborted)
+							mLock.wait();
+					}
+				} catch (final InterruptedException e) {
+					loge("Sleeping interrupted", e);
+				}
+			}
+			result = true;
+		} catch (final Exception e) {
+			Log.w(TAG, "An exception occurred while removing bond information", e);
+		}
+		return result;
+	}
+
+	/**
+	 * Waits until the notification will arrive. Returns the data returned by the notification. This method will block the thread if response is not ready or connection state will change from
+	 * {@link #STATE_CONNECTED_AND_READY}. If connection state will change, or an error will occur, an exception will be thrown.
+	 *
+	 * @return the value returned by the Control Point notification
+	 * @throws DeviceDisconnectedException
+	 * @throws DfuException
+	 * @throws UploadAbortedException
+	 */
+	private byte[] readNotificationResponse() throws DeviceDisconnectedException, DfuException, UploadAbortedException {
+		// do not clear the mReceiveData here. The response might already be obtained. Clear it in write request instead.
+		mError = 0;
+		try {
+			synchronized (mLock) {
+				while ((mReceivedData == null && mConnectionState == STATE_CONNECTED_AND_READY && mError == 0 && !mAborted) || mPaused)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			loge("Sleeping interrupted", e);
+		}
+		if (mAborted)
+			throw new UploadAbortedException();
+		if (mError != 0)
+			throw new DfuException("Unable to write Op Code", mError);
+		if (mConnectionState != STATE_CONNECTED_AND_READY)
+			throw new DeviceDisconnectedException("Unable to write Op Code", mConnectionState);
+		return mReceivedData;
+	}
+
+	/**
+	 * Creates or updates the notification in the Notification Manager. Sends broadcast with current progress to the activity.
+	 */
+	private void updateProgressNotification() {
+		final int progress = (int) (100.0f * mBytesSent / mImageSizeInBytes);
+		if (mLastProgress == progress)
+			return;
+
+		mLastProgress = progress;
+		updateProgressNotification(progress);
+	}
+
+	/**
+	 * Creates or updates the notification in the Notification Manager. Sends broadcast with given progress or error state to the activity.
+	 *
+	 * @param progress the current progress state or an error number, can be one of {@link #PROGRESS_CONNECTING}, {@link #PROGRESS_STARTING}, {@link #PROGRESS_ENABLING_DFU_MODE},
+	 *                 {@link #PROGRESS_VALIDATING}, {@link #PROGRESS_DISCONNECTING}, {@link #PROGRESS_COMPLETED} or {@link #ERROR_FILE_ERROR}, {@link #ERROR_FILE_INVALID} , etc
+	 */
+	private void updateProgressNotification(final int progress) {
+
+		// send progress or error broadcast
+		if (progress < ERROR_MASK)
+			sendProgressBroadcast(progress);
+		else
+			sendErrorBroadcast(progress);
+
+		if (mDisableNotification) return;
+		// create or update notification:
+
+		final String deviceAddress = mDeviceAddress;
+		final String deviceName = mDeviceName != null ? mDeviceName : getString(R.string.dfu_unknown_name);
+
+		// final Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_stat_notify_dfu); <- this looks bad on Android 5
+
+		final NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setSmallIcon(android.R.drawable.stat_sys_upload).setOnlyAlertOnce(true);//.setLargeIcon(largeIcon);
+		// Android 5
+		builder.setColor(Color.GRAY);
+
+		switch (progress) {
+			case PROGRESS_CONNECTING:
+				builder.setOngoing(true).setContentTitle(getString(R.string.dfu_status_connecting)).setContentText(getString(R.string.dfu_status_connecting_msg, deviceName)).setProgress(100, 0, true);
+				break;
+			case PROGRESS_STARTING:
+				builder.setOngoing(true).setContentTitle(getString(R.string.dfu_status_starting)).setContentText(getString(R.string.dfu_status_starting_msg, deviceName)).setProgress(100, 0, true);
+				break;
+			case PROGRESS_ENABLING_DFU_MODE:
+				builder.setOngoing(true).setContentTitle(getString(R.string.dfu_status_switching_to_dfu)).setContentText(getString(R.string.dfu_status_switching_to_dfu_msg, deviceName))
+						.setProgress(100, 0, true);
+				break;
+			case PROGRESS_VALIDATING:
+				builder.setOngoing(true).setContentTitle(getString(R.string.dfu_status_validating)).setContentText(getString(R.string.dfu_status_validating_msg, deviceName)).setProgress(100, 0, true);
+				break;
+			case PROGRESS_DISCONNECTING:
+				builder.setOngoing(true).setContentTitle(getString(R.string.dfu_status_disconnecting)).setContentText(getString(R.string.dfu_status_disconnecting_msg, deviceName))
+						.setProgress(100, 0, true);
+				break;
+			case PROGRESS_COMPLETED:
+				builder.setOngoing(false).setContentTitle(getString(R.string.dfu_status_completed)).setSmallIcon(android.R.drawable.stat_sys_upload_done)
+						.setContentText(getString(R.string.dfu_status_completed_msg)).setAutoCancel(true).setColor(0xFF00B81A);
+				break;
+			case PROGRESS_ABORTED:
+				builder.setOngoing(false).setContentTitle(getString(R.string.dfu_status_aborted)).setSmallIcon(android.R.drawable.stat_sys_upload_done)
+						.setContentText(getString(R.string.dfu_status_aborted_msg)).setAutoCancel(true);
+				break;
+			default:
+				if (progress >= ERROR_MASK) {
+					// progress is an error number
+					builder.setOngoing(false).setContentTitle(getString(R.string.dfu_status_error)).setSmallIcon(android.R.drawable.stat_sys_upload_done)
+							.setContentText(getString(R.string.dfu_status_error_msg)).setAutoCancel(true).setColor(Color.RED);
+				} else {
+					// progress is in percents
+					final String title = mPartsTotal == 1 ? getString(R.string.dfu_status_uploading) : getString(R.string.dfu_status_uploading_part, mPartCurrent, mPartsTotal);
+					final String text = (mFileType & TYPE_APPLICATION) > 0 ? getString(R.string.dfu_status_uploading_msg, deviceName) : getString(R.string.dfu_status_uploading_components_msg, deviceName);
+					builder.setOngoing(true).setContentTitle(title).setContentText(text).setProgress(100, progress, false);
+				}
+		}
+
+		// update the notification
+		final Intent intent = new Intent(this, getNotificationTarget());
+		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+		intent.putExtra(EXTRA_DEVICE_ADDRESS, deviceAddress);
+		intent.putExtra(EXTRA_DEVICE_NAME, deviceName);
+		intent.putExtra(EXTRA_PROGRESS, progress); // this may contains ERROR_CONNECTION_MASK bit!
+		final PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+		builder.setContentIntent(pendingIntent);
+
+		// Add Abort action to the notification
+		if (progress != PROGRESS_ABORTED && progress != PROGRESS_COMPLETED && progress < ERROR_MASK) {
+			final Intent abortIntent = new Intent(BROADCAST_ACTION);
+			abortIntent.putExtra(EXTRA_ACTION, ACTION_ABORT);
+			final PendingIntent pendingAbortIntent = PendingIntent.getBroadcast(this, 1, abortIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+			builder.addAction(R.drawable.ic_action_notify_cancel, getString(R.string.dfu_action_abort), pendingAbortIntent);
+		}
+
+		final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+		manager.notify(NOTIFICATION_ID, builder.build());
+	}
+
+	/**
+	 * This method must return the activity class that will be used to create the pending intent used as a content intent in the notification showing the upload progress.
+	 * The activity will be launched when user click the notification. DfuService will add {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag and the following extras:
+	 * <ul>
+	 * <li>{@link #EXTRA_DEVICE_ADDRESS} - target device address</li>
+	 * <li>{@link #EXTRA_DEVICE_NAME} - target device name</li>
+	 * <li>{@link #EXTRA_PROGRESS} - the connection state (values < 0)*, current progress (0-100) or error number if {@link #ERROR_MASK} bit set.</li>
+	 * </ul>
+	 * <p>
+	 * __________<br />
+	 * * - connection state constants:
+	 * <ul>
+	 * <li>{@link #PROGRESS_CONNECTING}</li>
+	 * <li>{@link #PROGRESS_DISCONNECTING}</li>
+	 * <li>{@link #PROGRESS_COMPLETED}</li>
+	 * <li>{@link #PROGRESS_ABORTED}</li>
+	 * <li>{@link #PROGRESS_STARTING}</li>
+	 * <li>{@link #PROGRESS_ENABLING_DFU_MODE}</li>
+	 * <li>{@link #PROGRESS_VALIDATING}</li>
+	 * </ul>
+	 * </p>
+	 *
+	 * @return the target activity class
+	 */
+	protected abstract Class<? extends Activity> getNotificationTarget();
+
+	private void sendProgressBroadcast(final int progress) {
+		final long now = SystemClock.elapsedRealtime();
+		final float speed = now - mLastProgressTime != 0 ? (float) (mBytesSent - mLastBytesSent) / (float) (now - mLastProgressTime) : 0.0f;
+		final float avgSpeed = now - mStartTime != 0 ? (float) mBytesSent / (float) (now - mStartTime) : 0.0f;
+		mLastProgressTime = now;
+		mLastBytesSent = mBytesSent;
+
+		final Intent broadcast = new Intent(BROADCAST_PROGRESS);
+		broadcast.putExtra(EXTRA_DATA, progress);
+		broadcast.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress);
+		broadcast.putExtra(EXTRA_PART_CURRENT, mPartCurrent);
+		broadcast.putExtra(EXTRA_PARTS_TOTAL, mPartsTotal);
+		broadcast.putExtra(EXTRA_SPEED_B_PER_MS, speed);
+		broadcast.putExtra(EXTRA_AVG_SPEED_B_PER_MS, avgSpeed);
+		LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+	}
+
+	private void sendErrorBroadcast(final int error) {
+		final Intent broadcast = new Intent(BROADCAST_ERROR);
+		if ((error & ERROR_CONNECTION_MASK) > 0) {
+			broadcast.putExtra(EXTRA_DATA, error & ~ERROR_CONNECTION_MASK);
+			broadcast.putExtra(EXTRA_ERROR_TYPE, ERROR_TYPE_COMMUNICATION);
+		} else if ((error & ERROR_CONNECTION_STATE_MASK) > 0) {
+			broadcast.putExtra(EXTRA_DATA, error & ~ERROR_CONNECTION_STATE_MASK);
+			broadcast.putExtra(EXTRA_ERROR_TYPE, ERROR_TYPE_COMMUNICATION_STATE);
+		} else if ((error & ERROR_REMOTE_MASK) > 0) {
+			broadcast.putExtra(EXTRA_DATA, error);
+			broadcast.putExtra(EXTRA_ERROR_TYPE, ERROR_TYPE_DFU_REMOTE);
+		} else {
+			broadcast.putExtra(EXTRA_DATA, error);
+			broadcast.putExtra(EXTRA_ERROR_TYPE, ERROR_TYPE_OTHER);
+		}
+		broadcast.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress);
+		LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+	}
+
+	private void sendLogBroadcast(final int level, final String message) {
+		final String fullMessage = "[DFU] " + message;
+		final Intent broadcast = new Intent(BROADCAST_LOG);
+		broadcast.putExtra(EXTRA_LOG_MESSAGE, fullMessage);
+		broadcast.putExtra(EXTRA_LOG_LEVEL, level);
+		broadcast.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress);
+		LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
+	}
+
+	/**
+	 * Initializes bluetooth adapter
+	 *
+	 * @return <code>true</code> if initialization was successful
+	 */
+	private boolean initialize() {
+		// For API level 18 and above, get a reference to BluetoothAdapter through
+		// BluetoothManager.
+		final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+		if (bluetoothManager == null) {
+			loge("Unable to initialize BluetoothManager.");
+			return false;
+		}
+
+		mBluetoothAdapter = bluetoothManager.getAdapter();
+		if (mBluetoothAdapter == null) {
+			loge("Unable to obtain a BluetoothAdapter.");
+			return false;
+		}
+
+		return true;
+	}
+
+	private void loge(final String message) { Log.e(TAG, message); }
+
+	private void loge(final String message, final Throwable e) { Log.e(TAG, message, e); }
+
+	private void logw(final String message) {
+	    // TODO(mcchou)
+	    //if (BuildConfig.DEBUG)
+	    //    Log.w(TAG, message);
+	}
+
+	private void logi(final String message) {
+	    // TODO(mcchou)
+	    //if (BuildConfig.DEBUG)
+	    //    Log.i(TAG, message);
+	}
+
+	private void logd(final String message) {
+	    // TODO(mcchou)
+	    //if (BuildConfig.DEBUG)
+	    //    Log.d(TAG, message);
+	}
+
+	private String parse(final byte[] data) {
+		if (data == null)
+			return "";
+
+		final int length = data.length;
+		if (length == 0)
+			return "";
+
+		final char[] out = new char[length * 3 - 1];
+		for (int j = 0; j < length; j++) {
+			int v = data[j] & 0xFF;
+			out[j * 3] = HEX_ARRAY[v >>> 4];
+			out[j * 3 + 1] = HEX_ARRAY[v & 0x0F];
+			if (j != length - 1)
+				out[j * 3 + 2] = '-';
+		}
+		return new String(out);
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuLogListener.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuLogListener.java
new file mode 100644
index 0000000..cb25392
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuLogListener.java
@@ -0,0 +1,45 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu;
+
+/**
+ * Listener for log events. This listener should be used instead of creating the BroadcastReceiver on your own.
+ * @see DfuServiceListenerHelper
+ */
+public interface DfuLogListener {
+	/**
+	 * Method called when a log event was sent from the DFU service.
+	 * @param deviceAddress the target device address
+	 * @param level the log level, one of:
+	 * 		<ul>
+	 * 		    <li>{@link DfuBaseService#LOG_LEVEL_DEBUG}</li>
+	 * 		    <li>{@link DfuBaseService#LOG_LEVEL_VERBOSE}</li>
+	 * 		    <li>{@link DfuBaseService#LOG_LEVEL_INFO}</li>
+	 * 		    <li>{@link DfuBaseService#LOG_LEVEL_APPLICATION}</li>
+	 * 		    <li>{@link DfuBaseService#LOG_LEVEL_WARNING}</li>
+	 * 		    <li>{@link DfuBaseService#LOG_LEVEL_ERROR}</li>
+	 * 		</ul>
+	 * @param message the log message
+	 */
+	public void onLogEvent(final String deviceAddress, final int level, final String message);
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuProgressListener.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuProgressListener.java
new file mode 100644
index 0000000..0a3ce7b
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuProgressListener.java
@@ -0,0 +1,113 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu;
+
+/**
+ * Listener for status, progress and error events. This listener should be used instead of creating the BroadcastReceiver on your own.
+ * @see DfuServiceListenerHelper
+ */
+public interface DfuProgressListener {
+
+	/**
+	 * Method called when the DFU service started connecting with the DFU target.
+	 * @param deviceAddress the target device address
+	 */
+	public void onDeviceConnecting(final String deviceAddress);
+
+	/**
+	 * Method called when the service has successfully connected, discovered services and found DFU service on the DFU target.
+	 * @param deviceAddress the target device address
+	 */
+	public void onDeviceConnected(final String deviceAddress);
+
+	/**
+	 * Method called when the DFU process is starting. This includes reading the DFU Version characteristic, sending DFU_START command as well as the Init packet, if set.
+	 * @param deviceAddress the target device address
+	 */
+	public void onDfuProcessStarting(final String deviceAddress);
+
+	/**
+	 * Method called when the DFU process was started and bytes about to be sent.
+	 * @param deviceAddress the target device address
+	 */
+	public void onDfuProcessStarted(final String deviceAddress);
+
+	/**
+	 * Method called when the service discovered that the DFU target is in the application mode and must be switched to DFU mode.
+	 * The switch command will be sent and the DFU process should start again. There will be no {@link #onDeviceDisconnected(String)} event following this call.
+	 * @param deviceAddress the target device address
+	 */
+	public void onEnablingDfuMode(final String deviceAddress);
+
+	/**
+	 * Method called during uploading the firmware. It will not be called twice with the same value of percent, however, in case of small firmware files, some values may be omitted.
+	 * @param deviceAddress the target device address
+	 * @param percent the current status of upload (0-99)
+	 * @param speed the current speed in bytes per millisecond
+	 * @param avgSpeed the average speed in bytes per millisecond
+	 * @param currentPart the number pf part being sent. In case the ZIP file contains a Soft Device and/or a Bootloader together with the application the SD+BL are sent as part 1,
+	 *                    then the service starts again and send the application as part 2
+	 * @param partsTotal total number of parts
+	 */
+	public void onProgressChanged(final String deviceAddress, final int percent, final float speed, final float avgSpeed, final int currentPart, final int partsTotal);
+
+	/**
+	 * Method called when the new firmware is being validated on the target device.
+	 * @param deviceAddress the target device address
+	 */
+	public void onFirmwareValidating(final String deviceAddress);
+
+	/**
+	 * Method called when the service started to disconnect from the target device.
+	 * @param deviceAddress the target device address
+	 */
+	public void onDeviceDisconnecting(final String deviceAddress);
+
+	/**
+	 * Method called when the service disconnected from the device. The device has been reset.
+	 * @param deviceAddress the target device address
+	 */
+	public void onDeviceDisconnected(final String deviceAddress);
+
+	/**
+	 * Method called when the DFU process succeeded.
+	 * @param deviceAddress the target device address
+	 */
+	public void onDfuCompleted(final String deviceAddress);
+
+	/**
+	 * Method called when the DFU process has been aborted.
+	 * @param deviceAddress the target device address
+	 */
+	public void onDfuAborted(final String deviceAddress);
+
+	/**
+	 * Method called when an error occur.
+	 * @param deviceAddress the target device address
+	 * @param error error number
+	 * @param errorType the error type, one of {@link DfuBaseService#ERROR_TYPE_COMMUNICATION_STATE}, {@link DfuBaseService#ERROR_TYPE_COMMUNICATION},
+	 * 			{@link DfuBaseService#ERROR_TYPE_DFU_REMOTE}, {@link DfuBaseService#ERROR_TYPE_OTHER}.
+	 * @param message the error message
+	 */
+	public void onError(final String deviceAddress, final int error, final int errorType, final String message);
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuProgressListenerAdapter.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuProgressListenerAdapter.java
new file mode 100644
index 0000000..6aba9c7
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuProgressListenerAdapter.java
@@ -0,0 +1,85 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu;
+
+public class DfuProgressListenerAdapter implements DfuProgressListener {
+	@Override
+	public void onDeviceConnecting(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onDeviceConnected(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onDfuProcessStarting(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onDfuProcessStarted(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onEnablingDfuMode(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onProgressChanged(final String deviceAddress, final int percent, final float speed, final float avgSpeed, final int currentPart, final int partsTotal) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onFirmwareValidating(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onDeviceDisconnecting(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onDeviceDisconnected(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onDfuCompleted(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onDfuAborted(final String deviceAddress) {
+		// empty default implementation
+	}
+
+	@Override
+	public void onError(final String deviceAddress, final int error, final int errorType, final String message) {
+		// empty default implementation
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuServiceInitiator.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuServiceInitiator.java
new file mode 100644
index 0000000..44fbd50
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuServiceInitiator.java
@@ -0,0 +1,300 @@
+/**
+ * **********************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ * <p/>
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * <p/>
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * <p/>
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * <p/>
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * <p/>
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * **********************************************************************************************************************************************
+ */
+
+package no.nordicsemi.android.dfu;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import java.security.InvalidParameterException;
+
+/**
+ * Starting the DfuService service requires a knowledge of some EXTRA_* constants used to pass parameters to the service.
+ * The DfuServiceInitiator class may be used to make this process easier. It provides simple API that covers all low lever operations.
+ */
+public class DfuServiceInitiator {
+	private final String deviceAddress;
+	private String deviceName;
+
+	private boolean disableNotification;
+
+	private Uri fileUri;
+	private String filePath;
+	private int fileResId;
+
+	private Uri initFileUri;
+	private String initFilePath;
+	private int initFileResId;
+
+	private String mimeType;
+	private int fileType = -1;
+
+	private boolean keepBond;
+
+	/**
+	 * Creates the builder. Use setZip(...), or setBinOrHex(...) methods to specify the file you want to upload.
+	 * In the latter case an init file may also be set using the setInitFile(...) method. Init files are required by DFU Bootloader version 0.5 or newer (SDK 7.0.0+).
+	 * @param deviceAddress the target device device address
+	 */
+	public DfuServiceInitiator(final String deviceAddress) {
+		this.deviceAddress = deviceAddress;
+	}
+
+	/**
+	 * Sets the device name. The device name is not required. It's written in the notification during the DFU process.
+	 * If not set the {@link no.nordicsemi.android.dfu.R.string#dfu_unknown_name R.string.dfu_unknown_name} value will be used.
+	 * @param name the device name (optional)
+	 * @return the builder
+	 */
+	public DfuServiceInitiator setDeviceName(final String name) {
+		this.deviceName = name;
+		return this;
+	}
+
+	/**
+	 * Sets whether the progress notification in the status bar should be disabled.
+	 * Defaults to false.
+	 * @param disableNotification whether to disable the notification
+	 * @return the builder
+	 */
+	public DfuServiceInitiator setDisableNotification(final boolean disableNotification) {
+		this.disableNotification = disableNotification;
+		return this;
+	}
+
+	/**
+	 * Sets whether the bond information should be preserver after flashing new application. This feature requires DFU Bootloader version 0.6 or newer (SDK 8.0.0+).
+	 * Please see the {@link DfuBaseService#EXTRA_KEEP_BOND} for more information regarding requirements. Remember that currently updating the Soft Device will remove the bond information.
+	 * @param keepBond whether the bond information should be preserved in the new application.
+	 * @return the builder
+	 */
+	public DfuServiceInitiator setKeepBond(final boolean keepBond) {
+		this.keepBond = keepBond;
+		return this;
+	}
+
+	/**
+	 * Sets the URI to the Distribution packet (ZIP) or to a ZIP file matching the deprecated naming convention.
+	 * @param uri the URI of the file
+	 * @return the builder
+	 * @see #setZip(String)
+	 * @see #setZip(int)
+	 */
+	public DfuServiceInitiator setZip(final Uri uri) {
+		return init(uri, null, 0, DfuBaseService.TYPE_AUTO, DfuBaseService.MIME_TYPE_ZIP);
+	}
+
+	/**
+	 * Sets the path to the Distribution packet (ZIP) or the a ZIP file matching the deprecated naming convention.
+	 * @param path path to the file
+	 * @return the builder
+	 * @see #setZip(Uri)
+	 * @see #setZip(int)
+	 */
+	public DfuServiceInitiator setZip(final String path) {
+		return init(null, path, 0, DfuBaseService.TYPE_AUTO, DfuBaseService.MIME_TYPE_ZIP);
+	}
+
+	/**
+	 * Sets the resource ID of the Distribution packet (ZIP) or the a ZIP file matching the deprecated naming convention. The file should be in the /res/raw folder.
+	 * @param rawResId file's resource ID
+	 * @return the builder
+	 * @see #setZip(Uri)
+	 * @see #setZip(String)
+	 */
+	public DfuServiceInitiator setZip(final int rawResId) {
+		return init(null, null, rawResId, DfuBaseService.TYPE_AUTO, DfuBaseService.MIME_TYPE_ZIP);
+	}
+
+	/**
+	 * Sets the URI or path of the ZIP file. If the URI and path are not null the URI will be used.
+	 * @param uri the URI of the file
+	 * @param path the path of the file
+	 * @return the builder
+	 */
+	public DfuServiceInitiator setZip(final Uri uri, final String path) {
+		return init(uri, path, 0, DfuBaseService.TYPE_AUTO, DfuBaseService.MIME_TYPE_ZIP);
+	}
+
+	/**
+	 * Sets the URI of the BIN or HEX file containing the new firmware.
+	 * For DFU Bootloader version 0.5 or newer the init file must be specified using one of {@link #setInitFile(Uri)} methods.
+	 * @param fileType the file type, a bit field created from:
+	 *  	<ul>
+	 * 		    <li>{@link DfuBaseService#TYPE_APPLICATION} - the Application will be sent</li>
+	 * 		    <li>{@link DfuBaseService#TYPE_SOFT_DEVICE} - he Soft Device will be sent</li>
+	 * 		    <li>{@link DfuBaseService#TYPE_BOOTLOADER} - the Bootloader will be sent</li>
+	 * 		</ul>
+	 * @param uri the URI of the file
+	 * @return the builder
+	 */
+	@Deprecated
+	public DfuServiceInitiator setBinOrHex(final int fileType, final Uri uri) {
+		if (fileType == DfuBaseService.TYPE_AUTO)
+			throw new UnsupportedOperationException("You must specify the file type");
+		return init(uri, null, 0, fileType, DfuBaseService.MIME_TYPE_OCTET_STREAM);
+	}
+
+	/**
+	 * Sets the URI of the BIN or HEX file containing the new firmware.
+	 * For DFU Bootloader version 0.5 or newer the init file must be specified using one of {@link #setInitFile(String)} methods.
+	 * @param fileType see {@link #setBinOrHex(int, Uri)} for details
+	 * @param path path to the file
+	 * @return the builder
+	 */
+	@Deprecated
+	public DfuServiceInitiator setBinOrHex(final int fileType, final String path) {
+		if (fileType == DfuBaseService.TYPE_AUTO)
+			throw new UnsupportedOperationException("You must specify the file type");
+		return init(null, path, 0, fileType, DfuBaseService.MIME_TYPE_OCTET_STREAM);
+	}
+
+	/**
+	 * Sets the URI or path to the BIN or HEX file containing the new firmware.
+	 * For DFU Bootloader version 0.5 or newer the init file must be specified using one of {@link #setInitFile(String)} methods.
+	 * @param fileType see {@link #setBinOrHex(int, Uri)} for details
+	 * @param uri the URI of the file
+	 * @param path path to the file
+	 * @return the builder
+	 * @deprecated The Distribution packet (ZIP) should be used for DFU Bootloader version 0.5 or newer
+	 */
+	@Deprecated
+	public DfuServiceInitiator setBinOrHex(final int fileType, final Uri uri, final String path) {
+		if (fileType == DfuBaseService.TYPE_AUTO)
+			throw new UnsupportedOperationException("You must specify the file type");
+		return init(uri, path, 0, fileType, DfuBaseService.MIME_TYPE_OCTET_STREAM);
+	}
+
+	/**
+	 * Sets the resource ID pointing the BIN or HEX file containing the new firmware. The file should be in the /res/raw folder.
+	 * For DFU Bootloader version 0.5 or newer the init file must be specified using one of {@link #setInitFile(int)} methods.
+	 * @param fileType see {@link #setBinOrHex(int, Uri)} for details
+	 * @param rawResId resource ID
+	 * @return the builder
+	 */
+	@Deprecated
+	public DfuServiceInitiator setBinOrHex(final int fileType, final int rawResId) {
+		if (fileType == DfuBaseService.TYPE_AUTO)
+			throw new UnsupportedOperationException("You must specify the file type");
+		return init(null, null, rawResId, fileType, DfuBaseService.MIME_TYPE_OCTET_STREAM);
+	}
+
+	/**
+	 * Sets the URI of the Init file. The init file for DFU Bootloader version pre-0.5 (SDK 4.3, 6.0, 6.1) contains only the CRC-16 of the firmware.
+	 * Bootloader version 0.5 or newer requires the Extended Init Packet.
+	 * @param initFileUri the URI of the init file
+	 * @return the builder
+	 */
+	@Deprecated
+	public DfuServiceInitiator setInitFile(final Uri initFileUri) {
+		return init(initFileUri, null, 0);
+	}
+
+	/**
+	 * Sets the path to the Init file. The init file for DFU Bootloader version pre-0.5 (SDK 4.3, 6.0, 6.1) contains only the CRC-16 of the firmware.
+	 * Bootloader version 0.5 or newer requires the Extended Init Packet.
+	 * @param initFilePath the path to the init file
+	 * @return the builder
+	 */
+	@Deprecated
+	public DfuServiceInitiator setInitFile(final String initFilePath) {
+		return init(null, initFilePath, 0);
+	}
+
+	/**
+	 * Sets the resource ID of the Init file. The init file for DFU Bootloader version pre-0.5 (SDK 4.3, 6.0, 6.1) contains only the CRC-16 of the firmware.
+	 * Bootloader version 0.5 or newer requires the Extended Init Packet.
+	 * @param initFileResId the resource ID of the init file
+	 * @return the builder
+	 */
+	@Deprecated
+	public DfuServiceInitiator setInitFile(final int initFileResId) {
+		return init(null, null, initFileResId);
+	}
+
+	/**
+	 * Sets the URI or path to the Init file. The init file for DFU Bootloader version pre-0.5 (SDK 4.3, 6.0, 6.1) contains only the CRC-16 of the firmware.
+	 * Bootloader version 0.5 or newer requires the Extended Init Packet. If the URI and path are not null the URI will be used.
+	 * @param initFileUri the URI of the init file
+	 * @return the builder
+	 */
+	@Deprecated
+	public DfuServiceInitiator setInitFile(final Uri initFileUri, final String initFilePath) {
+		return init(initFileUri, initFilePath, 0);
+	}
+
+	/**
+	 * Starts the DFU service.
+	 * @param context the application context
+	 * @param service the class derived from the BaseDfuService
+	 */
+	public void start(final Context context, final Class<? extends DfuBaseService> service) {
+		if (fileType == -1)
+			throw new UnsupportedOperationException("You must specify the firmware file before starting the service");
+
+		final Intent intent = new Intent(context, service);
+
+		intent.putExtra(DfuBaseService.EXTRA_DEVICE_ADDRESS, deviceAddress);
+		intent.putExtra(DfuBaseService.EXTRA_DEVICE_NAME, deviceName);
+		intent.putExtra(DfuBaseService.EXTRA_DISABLE_NOTIFICATION, disableNotification);
+		intent.putExtra(DfuBaseService.EXTRA_FILE_MIME_TYPE, mimeType);
+		intent.putExtra(DfuBaseService.EXTRA_FILE_TYPE, fileType);
+		intent.putExtra(DfuBaseService.EXTRA_FILE_URI, fileUri);
+		intent.putExtra(DfuBaseService.EXTRA_FILE_PATH, filePath);
+		intent.putExtra(DfuBaseService.EXTRA_FILE_RES_ID, fileResId);
+		intent.putExtra(DfuBaseService.EXTRA_INIT_FILE_URI, initFileUri);
+		intent.putExtra(DfuBaseService.EXTRA_INIT_FILE_PATH, initFilePath);
+		intent.putExtra(DfuBaseService.EXTRA_INIT_FILE_RES_ID, initFileResId);
+		intent.putExtra(DfuBaseService.EXTRA_KEEP_BOND, keepBond);
+
+		context.startService(intent);
+	}
+
+	private DfuServiceInitiator init(final Uri initFileUri, final String initFilePath, final int initFileResId) {
+		if (DfuBaseService.MIME_TYPE_ZIP.equals(mimeType))
+			throw new InvalidParameterException("Init file must be located inside the ZIP");
+
+		this.initFileUri = initFileUri;
+		this.initFilePath = initFilePath;
+		this.initFileResId = initFileResId;
+		return this;
+	}
+
+	private DfuServiceInitiator init(final Uri fileUri, final String filePath, final int fileResId, final int fileType, final String mimeType) {
+		this.fileUri = fileUri;
+		this.filePath = filePath;
+		this.fileResId = fileResId;
+		this.fileType = fileType;
+		this.mimeType = mimeType;
+
+		// If the MIME TYPE implies it's a ZIP file then the init file must be included in the file.
+		if (DfuBaseService.MIME_TYPE_ZIP.equals(mimeType)) {
+			this.initFileUri = null;
+			this.initFilePath = null;
+			this.initFileResId = 0;
+		}
+		return this;
+	}
+}
\ No newline at end of file
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuServiceListenerHelper.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuServiceListenerHelper.java
new file mode 100644
index 0000000..de32fef
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuServiceListenerHelper.java
@@ -0,0 +1,371 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.v4.content.LocalBroadcastManager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import no.nordicsemi.android.dfu.internal.scanner.BootloaderScanner;
+import no.nordicsemi.android.error.GattError;
+
+/**
+ * A helper class that should be used to register listeners for DFU Service broadcast events.
+ * The {@link DfuProgressListener} should be registered to listen for DFU status updates and errors, while the {@link DfuLogListener} listener receives the log updates.
+ * Listeners may be registered for a specified device (given with device address) or for any device. Keep in mind, that while updating the SoftDevice using the buttonless update
+ * the device may change its address in the bootloader mode.
+ *
+ * <p>Use {@link #registerProgressListener(Context, DfuProgressListener)} or {@link #registerLogListener(Context, DfuLogListener)} to register your listeners. Remember about unregistering them
+ * when your context is destroyed.</p>
+ */
+public class DfuServiceListenerHelper {
+	private static LogBroadcastReceiver mLogBroadcastReceiver;
+	private static ProgressBroadcastsReceiver mProgressBroadcastReceiver;
+
+	private static class LogBroadcastReceiver extends BroadcastReceiver {
+		private DfuLogListener mGlobalLogListener;
+		private Map<String, DfuLogListener> mListeners = new HashMap<>();
+
+		private void setLogListener(final DfuLogListener globalLogListener) {
+			this.mGlobalLogListener = globalLogListener;
+		}
+
+		private void setLogListener(final String deviceAddress, final DfuLogListener listener) {
+			// When using the buttonless update and updating the SoftDevice the application will be removed to make space for the new SoftDevice.
+			// The new bootloader will afterwards advertise with the address incremented by 1. We need to make sure that the listener will receive also events from this device.
+			mListeners.put(deviceAddress, listener);
+			mListeners.put(getIncrementedAddress(deviceAddress), listener); // assuming the address is a valid BLE address
+		}
+
+		private boolean removeLogListener(final DfuLogListener listener) {
+			if (mGlobalLogListener == listener)
+				mGlobalLogListener = null;
+
+			// We do it 2 times as the listener was added for 2 addresses
+			for (final Map.Entry<String, DfuLogListener> entry : mListeners.entrySet()) {
+				if (entry.getValue() == listener) {
+					mListeners.remove(entry.getKey());
+					break;
+				}
+			}
+			for (final Map.Entry<String, DfuLogListener> entry : mListeners.entrySet()) {
+				if (entry.getValue() == listener) {
+					mListeners.remove(entry.getKey());
+					break;
+				}
+			}
+
+			return mGlobalLogListener == null && mListeners.isEmpty();
+		}
+
+		@Override
+		public void onReceive(final Context context, final Intent intent) {
+			final String address = intent.getStringExtra(DfuBaseService.EXTRA_DEVICE_ADDRESS);
+
+			// Find proper listeners
+			final DfuLogListener globalListener = mGlobalLogListener;
+			final DfuLogListener deviceListener = mListeners.get(address);
+
+			if (globalListener == null && deviceListener == null)
+				return;
+
+			final int level = intent.getIntExtra(DfuBaseService.EXTRA_LOG_LEVEL, 0);
+			final String message = intent.getStringExtra(DfuBaseService.EXTRA_LOG_MESSAGE);
+
+			if (globalListener != null)
+				globalListener.onLogEvent(address, level, message);
+			if (deviceListener != null)
+				deviceListener.onLogEvent(address, level, message);
+		}
+	}
+
+	private static class ProgressBroadcastsReceiver extends BroadcastReceiver {
+		private DfuProgressListener mGlobalProgressListener;
+		private Map<String, DfuProgressListener> mListeners = new HashMap<>();
+
+		private void setProgressListener(final DfuProgressListener globalProgressListener) {
+			this.mGlobalProgressListener = globalProgressListener;
+		}
+
+		private void setProgressListener(final String deviceAddress, final DfuProgressListener listener) {
+			// When using the buttonless update and updating the SoftDevice the application will be removed to make space for the new SoftDevice.
+			// The new bootloader will afterwards advertise with the address incremented by 1. We need to make sure that the listener will receive also events from this device.
+			mListeners.put(deviceAddress, listener);
+			mListeners.put(getIncrementedAddress(deviceAddress), listener); // assuming the address is a valid BLE address
+		}
+
+		private boolean removeProgressListener(final DfuProgressListener listener) {
+			if (mGlobalProgressListener == listener)
+				mGlobalProgressListener = null;
+
+			// We do it 2 times as the listener was added for 2 addresses
+			for (final Map.Entry<String, DfuProgressListener> entry : mListeners.entrySet()) {
+				if (entry.getValue() == listener) {
+					mListeners.remove(entry.getKey());
+					break;
+				}
+			}
+			for (final Map.Entry<String, DfuProgressListener> entry : mListeners.entrySet()) {
+				if (entry.getValue() == listener) {
+					mListeners.remove(entry.getKey());
+					break;
+				}
+			}
+
+			return mGlobalProgressListener == null && mListeners.isEmpty();
+		}
+
+		@Override
+		public void onReceive(final Context context, final Intent intent) {
+			final String address = intent.getStringExtra(DfuBaseService.EXTRA_DEVICE_ADDRESS);
+
+			// Find proper listeners
+			final DfuProgressListener globalListener = mGlobalProgressListener;
+			final DfuProgressListener deviceListener = mListeners.get(address);
+
+			if (globalListener == null && deviceListener == null)
+				return;
+
+			final String action = intent.getAction();
+
+			switch (action) {
+				case DfuBaseService.BROADCAST_PROGRESS: {
+					final int progress = intent.getIntExtra(DfuBaseService.EXTRA_DATA, 0);
+					final float speed = intent.getFloatExtra(DfuBaseService.EXTRA_SPEED_B_PER_MS, 0.0f);
+					final float avgSpeed = intent.getFloatExtra(DfuBaseService.EXTRA_AVG_SPEED_B_PER_MS, 0.0f);
+					final int currentPart = intent.getIntExtra(DfuBaseService.EXTRA_PART_CURRENT, 0);
+					final int partsTotal = intent.getIntExtra(DfuBaseService.EXTRA_PARTS_TOTAL, 0);
+
+					switch (progress) {
+						case DfuBaseService.PROGRESS_CONNECTING:
+							if (globalListener != null)
+								globalListener.onDeviceConnecting(address);
+							if (deviceListener != null)
+								deviceListener.onDeviceConnecting(address);
+							break;
+						case DfuBaseService.PROGRESS_STARTING:
+							if (globalListener != null) {
+								globalListener.onDeviceConnected(address);
+								globalListener.onDfuProcessStarting(address);
+							}
+							if (deviceListener != null) {
+								deviceListener.onDeviceConnected(address);
+								deviceListener.onDfuProcessStarting(address);
+							}
+							break;
+						case DfuBaseService.PROGRESS_ENABLING_DFU_MODE:
+							if (globalListener != null)
+								globalListener.onEnablingDfuMode(address);
+							if (deviceListener != null)
+								deviceListener.onEnablingDfuMode(address);
+							break;
+						case DfuBaseService.PROGRESS_VALIDATING:
+							if (globalListener != null)
+								globalListener.onFirmwareValidating(address);
+							if (deviceListener != null)
+								deviceListener.onFirmwareValidating(address);
+							break;
+						case DfuBaseService.PROGRESS_DISCONNECTING:
+							if (globalListener != null)
+								globalListener.onDeviceDisconnecting(address);
+							if (deviceListener != null)
+								deviceListener.onDeviceDisconnecting(address);
+							break;
+						case DfuBaseService.PROGRESS_COMPLETED:
+							if (globalListener != null) {
+								globalListener.onDeviceDisconnected(address);
+								globalListener.onDfuCompleted(address);
+							}
+							if (deviceListener != null) {
+								deviceListener.onDeviceDisconnected(address);
+								deviceListener.onDfuCompleted(address);
+							}
+							break;
+						case DfuBaseService.PROGRESS_ABORTED:
+							if (globalListener != null) {
+								globalListener.onDeviceDisconnected(address);
+								globalListener.onDfuAborted(address);
+							}
+							if (deviceListener != null) {
+								deviceListener.onDeviceDisconnected(address);
+								deviceListener.onDfuAborted(address);
+							}
+							break;
+						default:
+							if (progress == 0) {
+								if (globalListener != null)
+									globalListener.onDfuProcessStarted(address);
+								if (deviceListener != null)
+									deviceListener.onDfuProcessStarted(address);
+							}
+							if (globalListener != null)
+								globalListener.onProgressChanged(address, progress, speed, avgSpeed, currentPart, partsTotal);
+							if (deviceListener != null)
+								deviceListener.onProgressChanged(address, progress, speed, avgSpeed, currentPart, partsTotal);
+							break;
+					}
+
+					break;
+				}
+				case DfuBaseService.BROADCAST_ERROR: {
+					final int error = intent.getIntExtra(DfuBaseService.EXTRA_DATA, 0);
+					final int errorType = intent.getIntExtra(DfuBaseService.EXTRA_ERROR_TYPE, 0);
+
+					if (globalListener != null)
+						globalListener.onDeviceDisconnected(address);
+					if (deviceListener != null)
+						deviceListener.onDeviceDisconnected(address);
+					switch (errorType) {
+						case DfuBaseService.ERROR_TYPE_COMMUNICATION_STATE:
+							if (globalListener != null)
+								globalListener.onError(address, error, errorType, GattError.parseConnectionError(error));
+							if (deviceListener != null)
+								deviceListener.onError(address, error, errorType, GattError.parseConnectionError(error));
+							break;
+						default:
+							if (globalListener != null)
+								globalListener.onError(address, error, errorType, GattError.parse(error));
+							if (deviceListener != null)
+								deviceListener.onError(address, error, errorType, GattError.parse(error));
+							break;
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Registers the {@link DfuProgressListener}. Registered listener will receive the progress events from the DFU service.
+	 * @param context the application context
+	 * @param listener the listener to register
+	 */
+	public static void registerProgressListener(final Context context, final DfuProgressListener listener) {
+		if (mProgressBroadcastReceiver == null) {
+			mProgressBroadcastReceiver = new ProgressBroadcastsReceiver();
+
+			final IntentFilter filter = new IntentFilter();
+			filter.addAction(DfuBaseService.BROADCAST_PROGRESS);
+			filter.addAction(DfuBaseService.BROADCAST_ERROR);
+			LocalBroadcastManager.getInstance(context).registerReceiver(mProgressBroadcastReceiver, filter);
+		}
+		mProgressBroadcastReceiver.setProgressListener(listener);
+	}
+
+	/**
+	 * Registers the {@link DfuProgressListener}. Registered listener will receive the progress events from the DFU service.
+	 * @param context the application context
+	 * @param listener the listener to register
+	 * @param deviceAddress the address of the device to receive updates from (or null if any device)
+	 */
+	public static void registerProgressListener(final Context context, final DfuProgressListener listener, final String deviceAddress) {
+		if (mProgressBroadcastReceiver == null) {
+			mProgressBroadcastReceiver = new ProgressBroadcastsReceiver();
+
+			final IntentFilter filter = new IntentFilter();
+			filter.addAction(DfuBaseService.BROADCAST_PROGRESS);
+			filter.addAction(DfuBaseService.BROADCAST_ERROR);
+			LocalBroadcastManager.getInstance(context).registerReceiver(mProgressBroadcastReceiver, filter);
+		}
+		mProgressBroadcastReceiver.setProgressListener(deviceAddress, listener);
+	}
+
+	/**
+	 * Unregisters the previously registered progress listener.
+	 * @param context the application context
+	 * @param listener the listener to unregister
+	 */
+	public static void unregisterProgressListener(final Context context, final DfuProgressListener listener) {
+		if (mProgressBroadcastReceiver != null) {
+			final boolean empty = mProgressBroadcastReceiver.removeProgressListener(listener);
+
+			if (empty) {
+				LocalBroadcastManager.getInstance(context).unregisterReceiver(mProgressBroadcastReceiver);
+				mProgressBroadcastReceiver = null;
+			}
+		}
+	}
+
+	/**
+	 * Registers the {@link DfuLogListener}. Registered listener will receive the log events from the DFU service.
+	 * @param context the application context
+	 * @param listener the listener to register
+	 */
+	public static void registerLogListener(final Context context, final DfuLogListener listener) {
+		if (mLogBroadcastReceiver == null) {
+			mLogBroadcastReceiver = new LogBroadcastReceiver();
+
+			final IntentFilter filter = new IntentFilter();
+			filter.addAction(DfuBaseService.BROADCAST_LOG);
+			LocalBroadcastManager.getInstance(context).registerReceiver(mLogBroadcastReceiver, filter);
+		}
+		mLogBroadcastReceiver.setLogListener(listener);
+	}
+
+	/**
+	 * Registers the {@link DfuLogListener}. Registered listener will receive the log events from the DFU service.
+	 * @param context the application context
+	 * @param listener the listener to register
+	 * @param deviceAddress the address of the device to receive updates from (or null if any device)
+	 */
+	public static void registerLogListener(final Context context, final DfuLogListener listener, final String deviceAddress) {
+		if (mLogBroadcastReceiver == null) {
+			mLogBroadcastReceiver = new LogBroadcastReceiver();
+
+			final IntentFilter filter = new IntentFilter();
+			filter.addAction(DfuBaseService.BROADCAST_LOG);
+			LocalBroadcastManager.getInstance(context).registerReceiver(mLogBroadcastReceiver, filter);
+		}
+		mLogBroadcastReceiver.setLogListener(deviceAddress, listener);
+	}
+
+	/**
+	 * Unregisters the previously registered log listener.
+	 * @param context the application context
+	 * @param listener the listener to unregister
+	 */
+	public static void unregisterLogListener(final Context context, final DfuLogListener listener) {
+		if (mLogBroadcastReceiver != null) {
+			final boolean empty = mLogBroadcastReceiver.removeLogListener(listener);
+
+			if (empty) {
+				LocalBroadcastManager.getInstance(context).unregisterReceiver(mLogBroadcastReceiver);
+				mLogBroadcastReceiver = null;
+			}
+		}
+	}
+
+	private static String getIncrementedAddress(final String deviceAddress) {
+		final String firstBytes = deviceAddress.substring(0, 15);
+		final String lastByte = deviceAddress.substring(15); // assuming that the device address is correct
+		final String lastByteIncremented = String.format("%02X", (Integer.valueOf(lastByte, 16) + BootloaderScanner.ADDRESS_DIFF) & 0xFF);
+		return firstBytes + lastByteIncremented;
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuSettingsConstants.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuSettingsConstants.java
new file mode 100644
index 0000000..fb3935b
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/DfuSettingsConstants.java
@@ -0,0 +1,83 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+public interface DfuSettingsConstants {
+	/**
+	 * This property must contain a boolean value.
+	 * <p>If true (default) the Packet Receipt Notification procedure will be enabled. See DFU documentation on http://infocenter.nordicsemi.com for more details.
+	 * The number of packets before receiving a Packet Receipt Notification is set with property {@link #SETTINGS_NUMBER_OF_PACKETS}.
+	 */
+	public static final String SETTINGS_PACKET_RECEIPT_NOTIFICATION_ENABLED = "settings_packet_receipt_notification_enabled";
+
+	/**
+	 * This property must contain a positive integer value, usually from range 1-200.
+	 * <p>The default value is {@value #SETTINGS_NUMBER_OF_PACKETS_DEFAULT}. Setting it to 0 will disable the Packet Receipt Notification procedure.
+	 * When sending a firmware using the DFU procedure the service will send this number of packets before waiting for a notification.
+	 * Packet Receipt Notifications are used to synchronize the sender with receiver. On Android, calling {@link android.bluetooth.BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic)}
+	 * will simply add the packet to outgoing queue before returning the callback. Adding the next packet in the callback is much faster than the real transmission (also the speed depends on
+	 * the device chip manufacturer) and the queue may reach its limit. When does, the transmission stops and Android Bluetooth hangs. Using PRN procedure eliminates this problem as
+	 * the notification is send when all packets were delivered the queue is empty.
+	 */
+	public static final String SETTINGS_NUMBER_OF_PACKETS = "settings_number_of_packets";
+
+	/**
+	 * The default value of {@link #SETTINGS_NUMBER_OF_PACKETS} property. Different phones sent a different number of packets each connection interval. The values are (for tested phones):
+	 * <ul>
+	 *     <li>1 packet - Nexus 4 and Nexus 7 and others</li>
+	 *     <li>4 packets - Nexus 5 and Nexus 6 and others</li>
+	 *     <li>6 packets - LG F60 and others</li>
+	 * </ul>
+	 * The least common multiplier is 12 which is reasonably small. You may try other values, like 24 etc. Values higher than ~300 may cause the Bluetooth outgoing queue overflow error.
+	 */
+	public static final int SETTINGS_NUMBER_OF_PACKETS_DEFAULT = 3;
+
+	/**
+	 * This property must contain an integer value.
+	 * <p>Size of the MBR (Master Boot Record) for the target chip. This applies only if you are using HEX files. The HEX_to_BIN parser included in the library will skip the addresses
+	 * from 0 to this value. By default for nRF51 and the SoftDevice S110 this value is equal 4096 (0x1000 HEX) and for nRF52 has to be changed to 12288 (0x3000). If you want to send
+	 * a firmware in HEX onto another MCU via nRF chip, set this value to 0.
+	 * <p>If you are using the PC nrf util tool to create a ZIP Distribution Packet with the firmware and Init Packet this option does not apply as the nrf tool will convert HEX to BIN itself.
+	 */
+	public static final String SETTINGS_MBR_SIZE = "settings_mbr_size";
+
+	/**
+	 * The default value of the MBR size.
+	 * @see #SETTINGS_DEFAULT_MBR_SIZE
+	 */
+	public static final int SETTINGS_DEFAULT_MBR_SIZE = 0x1000;
+
+	/**
+	 * This property must contain a boolean value.
+	 * <p>The {@link DfuBaseService}, after connected to a DFU target will check whether it is in application or in DFU bootloader mode. For DFU implementations from SDK 7.0 or newer
+	 * this is done by reading the value of DFU Version characteristic. It the returned value is equal to 0x0100 (major = 0, minor = 1) it means that we are in the application mode and
+	 * jump to the bootloader mode is required.
+	 * <p>However, for DFU implementations from older SDKs, where there was no DFU Version characteristic, the service must guess. If this option is set to false (default) it will count
+	 * number of device's services. If the count is equal to 3 (Generic Access, Generic Attribute, DFU Service) it will assume that it's in DFU mode. If greater than 3 - in app mode.
+	 * This guessing may not be always correct. One situation may be when the nRF chip is used to flash update on external MCU using DFU. The DFU procedure may be implemented in the
+	 * application, which may (and usually does) have more services. In such case set the value of this property to true.
+	 */
+	public static final String SETTINGS_ASSUME_DFU_NODE = "settings_assume_dfu_mode";
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/ArchiveInputStream.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/ArchiveInputStream.java
new file mode 100644
index 0000000..62bd9bf
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/ArchiveInputStream.java
@@ -0,0 +1,456 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal;
+
+import com.google.gson.Gson;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import no.nordicsemi.android.dfu.DfuBaseService;
+import no.nordicsemi.android.dfu.internal.manifest.FileInfo;
+import no.nordicsemi.android.dfu.internal.manifest.Manifest;
+import no.nordicsemi.android.dfu.internal.manifest.ManifestFile;
+import no.nordicsemi.android.dfu.internal.manifest.SoftDeviceBootloaderFileInfo;
+
+/**
+ * <p>Reads the firmware files from the a ZIP file. The ZIP file must be either created using the <b>nrf utility</b> tool, available together with Master Control Panel v3.8.0+,
+ * or follow the backward compatibility syntax: must contain only files with names: application.hex/bin, softdevice.hex/dat or bootloader.hex/bin, optionally also application.dat
+ * and/or system.dat with init packets.</p>
+ * <p>The ArchiveInputStream will read only files with types specified by <b>types</b> parameter of the constructor.</p>
+ */
+public class ArchiveInputStream extends ZipInputStream {
+	/** The name of the manifest file is fixed. */
+	private static final String MANIFEST = "manifest.json";
+	// Those file names are for backwards compatibility mode
+	private static final String SOFTDEVICE_HEX = "softdevice.hex";
+	private static final String SOFTDEVICE_BIN = "softdevice.bin";
+	private static final String BOOTLOADER_HEX = "bootloader.hex";
+	private static final String BOOTLOADER_BIN = "bootloader.bin";
+	private static final String APPLICATION_HEX = "application.hex";
+	private static final String APPLICATION_BIN = "application.bin";
+	private static final String SYSTEM_INIT = "system.dat";
+	private static final String APPLICATION_INIT = "application.dat";
+
+	/** Contains bytes arrays with BIN files. HEX files are converted to BIN before being added to this map. */
+	private Map<String, byte[]> entries;
+	private Manifest manifest;
+
+	private byte[] applicationBytes;
+	private byte[] softDeviceBytes;
+	private byte[] bootloaderBytes;
+	private byte[] softDeviceAndBootloaderBytes;
+	private byte[] systemInitBytes;
+	private byte[] applicationInitBytes;
+	private byte[] currentSource;
+	private int bytesReadFromCurrentSource;
+	private int softDeviceSize;
+	private int bootloaderSize;
+	private int applicationSize;
+	private int bytesRead;
+
+	/**
+	 * <p>
+	 * The ArchiveInputStream read HEX or BIN files from the Zip stream. It may skip some of them, depending on the value of the types parameter.
+	 * This is useful if the DFU service wants to send the Soft Device and Bootloader only, and then the Application in the following connection, despite
+	 * the ZIP file contains all 3 HEX/BIN files.
+	 * When types is equal to {@link DfuBaseService#TYPE_AUTO} all present files are read.
+	 * </p>
+	 * <p>
+	 * Use bit combination of the following types:
+	 * <ul>
+	 * <li>{@link DfuBaseService#TYPE_SOFT_DEVICE}</li>
+	 * <li>{@link DfuBaseService#TYPE_BOOTLOADER}</li>
+	 * <li>{@link DfuBaseService#TYPE_APPLICATION}</li>
+	 * <li>{@link DfuBaseService#TYPE_AUTO}</li>
+	 * </ul>
+	 * </p>
+	 * 
+	 * @param stream
+	 *            the Zip Input Stream
+	 * @param mbrSize
+	 *            The size of the MRB segment (Master Boot Record) on the device. The parser will cut data from addresses below that number from all HEX files.
+	 * @param types
+	 *            File types that are to be read from the ZIP. Use {@link DfuBaseService#TYPE_APPLICATION} etc.
+	 * @throws java.io.IOException
+	 */
+	public ArchiveInputStream(final InputStream stream, final int mbrSize, final int types) throws IOException {
+		super(stream);
+
+		this.entries = new HashMap<>();
+		this.bytesRead = 0;
+		this.bytesReadFromCurrentSource = 0;
+
+		try {
+			/*
+			 * This method reads all entries from the ZIP file and puts them to entries map.
+			 * The 'manifest.json' file, if exists, is converted to the manifestData String.
+			 */
+			parseZip(mbrSize);
+
+			/*
+			 * Let's read and parse the 'manifest.json' file.
+			 */
+			if (manifest != null) {
+				boolean valid = false;
+
+				// Read the application
+				if (manifest.getApplicationInfo() != null && (types == DfuBaseService.TYPE_AUTO || (types & DfuBaseService.TYPE_APPLICATION) > 0)) {
+					final FileInfo application = manifest.getApplicationInfo();
+					applicationBytes = entries.get(application.getBinFileName());
+					applicationInitBytes = entries.get(application.getDatFileName());
+
+					if (applicationBytes == null)
+						throw new IOException("Application file " + application.getBinFileName() + " not found.");
+					applicationSize = applicationBytes.length;
+					currentSource = applicationBytes;
+					valid = true;
+				}
+
+				// Read the Bootloader
+				if (manifest.getBootloaderInfo() != null && (types == DfuBaseService.TYPE_AUTO || (types & DfuBaseService.TYPE_BOOTLOADER) > 0)) {
+					if (systemInitBytes != null)
+						throw new IOException("Manifest: softdevice and bootloader specified. Use softdevice_bootloader instead.");
+
+					final FileInfo bootloader = manifest.getBootloaderInfo();
+					bootloaderBytes = entries.get(bootloader.getBinFileName());
+					systemInitBytes = entries.get(bootloader.getDatFileName());
+
+					if (bootloaderBytes == null)
+						throw new IOException("Bootloader file " + bootloader.getBinFileName() + " not found.");
+					bootloaderSize = bootloaderBytes.length;
+					currentSource = bootloaderBytes;
+					valid = true;
+				}
+
+				// Read the Soft Device
+				if (manifest.getSoftdeviceInfo() != null && (types == DfuBaseService.TYPE_AUTO || (types & DfuBaseService.TYPE_SOFT_DEVICE) > 0)) {
+					final FileInfo softdevice = manifest.getSoftdeviceInfo();
+					softDeviceBytes = entries.get(softdevice.getBinFileName());
+					systemInitBytes = entries.get(softdevice.getDatFileName());
+
+					if (softDeviceBytes == null)
+						throw new IOException("SoftDevice file " + softdevice.getBinFileName() + " not found.");
+					softDeviceSize = softDeviceBytes.length;
+					currentSource = softDeviceBytes;
+					valid = true;
+				}
+
+				// Read the combined Soft Device and Bootloader
+				if (manifest.getSoftdeviceBootloaderInfo() != null && (types == DfuBaseService.TYPE_AUTO ||
+						((types & DfuBaseService.TYPE_SOFT_DEVICE) > 0) && (types & DfuBaseService.TYPE_BOOTLOADER) > 0)) {
+					if (systemInitBytes != null)
+						throw new IOException("Manifest: The softdevice_bootloader may not be used together with softdevice or bootloader.");
+
+					final SoftDeviceBootloaderFileInfo system = manifest.getSoftdeviceBootloaderInfo();
+					softDeviceAndBootloaderBytes = entries.get(system.getBinFileName());
+					systemInitBytes = entries.get(system.getDatFileName());
+
+					if (softDeviceAndBootloaderBytes == null)
+						throw new IOException("File " + system.getBinFileName() + " not found.");
+					softDeviceSize = system.getSoftdeviceSize();
+					bootloaderSize = system.getBootloaderSize();
+					currentSource = softDeviceAndBootloaderBytes;
+					valid = true;
+				}
+
+				if (!valid) {
+					throw new IOException("Manifest file must specify at least one file.");
+				}
+			} else {
+				/*
+				 * Compatibility mode. The 'manifest.json' file does not exist.
+				 *
+				 * In that case the ZIP file must contain one or more of the following files:
+				 *
+				 * - application.hex/dat
+				 *     + application.dat
+				 * - softdevice.hex/dat
+				 * - bootloader.hex/dat
+				 *     + system.dat
+				 */
+				boolean valid = false;
+				// Search for the application
+				if (types == DfuBaseService.TYPE_AUTO || (types & DfuBaseService.TYPE_APPLICATION) > 0) {
+					applicationBytes = entries.get(APPLICATION_HEX); // the entry bytes has already been converted to BIN, just the name remained.
+					if (applicationBytes == null)
+						applicationBytes = entries.get(APPLICATION_BIN);
+					if (applicationBytes != null) {
+						applicationSize = applicationBytes.length;
+						applicationInitBytes = entries.get(APPLICATION_INIT);
+						currentSource = applicationBytes;
+						valid = true;
+					}
+				}
+
+				// Search for theBootloader
+				if (types == DfuBaseService.TYPE_AUTO || (types & DfuBaseService.TYPE_BOOTLOADER) > 0) {
+					bootloaderBytes = entries.get(BOOTLOADER_HEX); // the entry bytes has already been converted to BIN, just the name remained.
+					if (bootloaderBytes == null)
+						bootloaderBytes = entries.get(BOOTLOADER_BIN);
+					if (bootloaderBytes != null) {
+						bootloaderSize = bootloaderBytes.length;
+						systemInitBytes = entries.get(SYSTEM_INIT);
+						currentSource = bootloaderBytes;
+						valid = true;
+					}
+				}
+
+				// Search for the Soft Device
+				if (types == DfuBaseService.TYPE_AUTO || (types & DfuBaseService.TYPE_SOFT_DEVICE) > 0) {
+					softDeviceBytes = entries.get(SOFTDEVICE_HEX); // the entry bytes has already been converted to BIN, just the name remained.
+					if (softDeviceBytes == null)
+						softDeviceBytes = entries.get(SOFTDEVICE_BIN);
+					if (softDeviceBytes != null) {
+						softDeviceSize = softDeviceBytes.length;
+						systemInitBytes = entries.get(SYSTEM_INIT);
+						currentSource = softDeviceBytes;
+						valid = true;
+					}
+				}
+
+				if (!valid) {
+					throw new IOException("The ZIP file must contain an Application, a Soft Device and/or a Bootloader.");
+				}
+			}
+		} finally {
+			super.close();
+		}
+	}
+
+	/**
+	 * Reads all files into byte arrays.
+	 * Here we don't know whether the ZIP file is valid.
+	 *
+	 * The ZIP file is valid when contains a 'manifest.json' file and all BIN and DAT files that are specified in the manifest.
+	 *
+	 * For backwards compatibility ArchiveInputStream supports also ZIP archives without 'manifest.json' file
+	 * but than it MUST include at least one of the following files: softdevice.bin/hex, bootloader.bin/hex, application.bin/hex.
+	 * To support the init packet such ZIP file should contain also application.dat and/or system.dat (with the CRC16 of a SD, BL or SD+BL together).
+	 */
+	private void parseZip(int mbrSize) throws IOException {
+		final byte[] buffer = new byte[1024];
+		String manifestData = null;
+
+		ZipEntry ze;
+		while ((ze = getNextEntry()) != null) {
+			final String filename = ze.getName();
+
+			// Read file content to byte array
+			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+			int count;
+			while ((count = super.read(buffer)) != -1) {
+				baos.write(buffer, 0, count);
+			}
+			byte[] source = baos.toByteArray();
+
+			// In case of HEX file convert it to BIN
+			if (filename.toLowerCase(Locale.US).endsWith("hex")) {
+				final HexInputStream is = new HexInputStream(source, mbrSize);
+				source = new byte[is.available()];
+				is.read(source);
+				is.close();
+			}
+
+			// Save the file content either as a manifest data or by adding it to entries
+			if (MANIFEST.equals(filename))
+				manifestData = new String(source, "UTF-8");
+			else
+				entries.put(filename, source);
+		}
+
+		if (manifestData != null) {
+			final ManifestFile manifestFile = new Gson().fromJson(manifestData, ManifestFile.class);
+			manifest = manifestFile.getManifest();
+		}
+	}
+
+	@Override
+	public void close() throws IOException {
+		softDeviceBytes = null;
+		bootloaderBytes = null;
+		softDeviceBytes = null;
+		softDeviceAndBootloaderBytes = null;
+		softDeviceSize = bootloaderSize = applicationSize = 0;
+		currentSource = null;
+		bytesRead = bytesReadFromCurrentSource = 0;
+		super.close();
+	}
+
+	@Override
+	public int read(final byte[] buffer) throws IOException {
+		int maxSize = currentSource.length - bytesReadFromCurrentSource;
+		int size = buffer.length <= maxSize ? buffer.length : maxSize;
+		System.arraycopy(currentSource, bytesReadFromCurrentSource, buffer, 0, size);
+		bytesReadFromCurrentSource += size;
+		if (buffer.length > size) {
+			if (startNextFile() == null) {
+				bytesRead += size;
+				return size;
+			}
+
+			maxSize = currentSource.length;
+			final int nextSize = buffer.length - size <= maxSize ? buffer.length - size : maxSize;
+			System.arraycopy(currentSource, 0, buffer, size, nextSize);
+			bytesReadFromCurrentSource += nextSize;
+			size += nextSize;
+		}
+		bytesRead += size;
+		return size;
+	}
+
+	/**
+	 * Returns the manifest object if it was specified in the ZIP file.
+	 * @return the manifest object
+	 */
+	public Manifest getManifest() {
+		return manifest;
+	}
+
+	/**
+	 * Returns the content type based on the content of the ZIP file. The content type may be truncated using {@link #setContentType(int)}.
+	 * 
+	 * @return a bit field of {@link DfuBaseService#TYPE_SOFT_DEVICE TYPE_SOFT_DEVICE}, {@link DfuBaseService#TYPE_BOOTLOADER TYPE_BOOTLOADER} and {@link DfuBaseService#TYPE_APPLICATION
+	 *         TYPE_APPLICATION}
+	 */
+	public int getContentType() {
+		byte b = 0;
+		if (softDeviceSize > 0)
+			b |= DfuBaseService.TYPE_SOFT_DEVICE;
+		if (bootloaderSize > 0)
+			b |= DfuBaseService.TYPE_BOOTLOADER;
+		if (applicationSize > 0)
+			b |= DfuBaseService.TYPE_APPLICATION;
+		return b;
+	}
+
+	/**
+	 * Truncates the current content type. May be used to hide some files, f.e. to send Soft Device and Bootloader without Application or only the Application.
+	 * 
+	 * @param type
+	 *            the new type
+	 * @return the final type after truncating
+	 */
+	public int setContentType(final int type) {
+		if (bytesRead > 0)
+			throw new UnsupportedOperationException("Content type must not be change after reading content");
+
+		final int t = getContentType() & type;
+
+		if ((t & DfuBaseService.TYPE_SOFT_DEVICE) == 0) {
+			softDeviceBytes = null;
+			if (softDeviceAndBootloaderBytes != null) {
+				softDeviceAndBootloaderBytes = null;
+				bootloaderSize = 0;
+			}
+			softDeviceSize = 0;
+		}
+		if ((t & DfuBaseService.TYPE_BOOTLOADER) == 0) {
+			bootloaderBytes = null;
+			if (softDeviceAndBootloaderBytes != null) {
+				softDeviceAndBootloaderBytes = null;
+				softDeviceSize = 0;
+			}
+			bootloaderSize = 0;
+		}
+		if ((t & DfuBaseService.TYPE_APPLICATION) == 0) {
+			applicationBytes = null;
+			applicationSize = 0;
+		}
+		return t;
+	}
+
+	/**
+	 * Sets the currentSource to the new file or to <code>null</code> if the last file has been transmitted.
+	 * 
+	 * @return the new source, the same as {@link #currentSource}
+	 */
+	private byte[] startNextFile() {
+		byte[] ret;
+		if (currentSource == softDeviceBytes && bootloaderBytes != null) {
+			ret = currentSource = bootloaderBytes;
+		} else if (currentSource != applicationBytes && applicationBytes != null) {
+			ret = currentSource = applicationBytes;
+		} else {
+			ret = currentSource = null;
+		}
+		bytesReadFromCurrentSource = 0;
+		return ret;
+	}
+
+	@Override
+	/**
+	 * Returns the number of bytes that has not been read yet. This value includes only firmwares matching the content type set by the construcotor or the {@link #setContentType(int)} method.
+	 */
+	public int available() {
+		return softDeviceSize + bootloaderSize + applicationSize - bytesRead;
+	}
+
+	/**
+	 * Returns the total size of the SoftDevice firmware. In case the firmware was given as a HEX, this method returns the size of the BIN content of the file.
+	 * @return the size of the SoftDevice firmware (BIN part)
+	 */
+	public int softDeviceImageSize() {
+		return softDeviceSize;
+	}
+
+	/**
+	 * Returns the total size of the Bootloader firmware. In case the firmware was given as a HEX, this method returns the size of the BIN content of the file.
+	 * @return the size of the Bootloader firmware (BIN part)
+	 */
+	public int bootloaderImageSize() {
+		return bootloaderSize;
+	}
+
+	/**
+	 * Returns the total size of the Application firmware. In case the firmware was given as a HEX, this method returns the size of the BIN content of the file.
+	 * @return the size of the Application firmware (BIN part)
+	 */
+	public int applicationImageSize() {
+		return applicationSize;
+	}
+
+	/**
+	 * Returns the content of the init file for SoftDevice and/or Bootloader. When both SoftDevice and Bootloader are present in the ZIP file (as two files using the compatibility mode
+	 * or as one file using the new Distribution packet) the system init contains validation data for those two files combined (e.g. the CRC value). This method may return
+	 * <code>null</code> if there is no SoftDevice nor Bootloader in the ZIP or the DAT file is not present there.
+	 * @return the content of the init packet for SoftDevice and/or Bootloader
+	 */
+	public byte[] getSystemInit() {
+		return systemInitBytes;
+	}
+
+	/**
+	 * Returns the content of the init file for the Application or <code>null</code> if no application file in the ZIP, or the DAT file is not provided.
+	 * @return the content of the init packet for Application
+	 */
+	public byte[] getApplicationInit() {
+		return applicationInitBytes;
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/HexInputStream.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/HexInputStream.java
new file mode 100644
index 0000000..50b5346
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/HexInputStream.java
@@ -0,0 +1,345 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import no.nordicsemi.android.dfu.internal.exception.HexFileValidationException;
+
+/**
+ * Reads the binary content from the HEX file using IntelHex standard: http://www.interlog.com/~speff/usefulinfo/Hexfrmt.pdf.
+ * Truncates the HEX file from all meta data and returns only the BIN content.
+ * <p>
+ * In nRF51 chips memory a SoftDevice starts at address 0x1000. From 0x0000 to 0x1000 there is MBR sector (since SoftDevice 7.0.0) which should not be transmitted using DFU. Therefore this class skips
+ * all data from addresses below 0x1000.
+ * </p>
+ */
+public class HexInputStream extends FilterInputStream {
+	private final int LINE_LENGTH = 128;
+
+	private final byte[] localBuf;
+	private int localPos;
+	private int pos;
+	private int size;
+	private int lastAddress;
+	private int available, bytesRead;
+	private final int MBRSize;
+
+	/**
+	 * Creates the HEX Input Stream. The constructor calculates the size of the BIN content which is available through {@link #sizeInBytes()}. If HEX file is invalid then the bin size is 0.
+	 * 
+	 * @param in
+	 *            the input stream to read from
+	 * @param mbrSize
+	 *            The MBR (Master Boot Record) size in bytes. Data with addresses below than number will be trimmed and not transferred to DFU target.
+	 * @throws HexFileValidationException
+	 *             if HEX file is invalid. F.e. there is no semicolon (':') on the beginning of each line.
+	 * @throws java.io.IOException
+	 *             if the stream is closed or another IOException occurs.
+	 */
+	public HexInputStream(final InputStream in, final int mbrSize) throws HexFileValidationException, IOException {
+		super(new BufferedInputStream(in));
+		this.localBuf = new byte[LINE_LENGTH];
+		this.localPos = LINE_LENGTH; // we are at the end of the local buffer, new one must be obtained
+		this.size = localBuf.length;
+		this.lastAddress = 0;
+		this.MBRSize = mbrSize;
+
+		this.available = calculateBinSize(mbrSize);
+	}
+
+	public HexInputStream(final byte[] data, final int mbrSize) throws HexFileValidationException, IOException {
+		super(new ByteArrayInputStream(data));
+		this.localBuf = new byte[LINE_LENGTH];
+		this.localPos = LINE_LENGTH; // we are at the end of the local buffer, new one must be obtained
+		this.size = localBuf.length;
+		this.lastAddress = 0;
+		this.MBRSize = mbrSize;
+
+		this.available = calculateBinSize(mbrSize);
+	}
+
+	private int calculateBinSize(final int mbrSize) throws IOException {
+		int binSize = 0;
+		final InputStream in = this.in;
+		in.mark(in.available());
+
+		int b, lineSize, offset, type;
+		int lastBaseAddress = 0; // last Base Address, default 0 
+		int lastAddress;
+		try {
+			b = in.read();
+			while (true) {
+				checkComma(b);
+
+				lineSize = readByte(in); // reading the length of the data in this line
+				offset = readAddress(in);// reading the offset
+				type = readByte(in); // reading the line type
+				switch (type) {
+				case 0x01:
+					// end of file
+					return binSize;
+				case 0x04: {
+					// extended linear address record
+					/*
+					 * The HEX file may contain jump to different addresses. The MSB of LBA (Linear Base Address) is given using the line type 4.
+					 * We only support files where bytes are located together, no jumps are allowed. Therefore the newULBA may be only lastULBA + 1 (or any, if this is the first line of the HEX)
+					 */
+					final int newULBA = readAddress(in);
+					if (binSize > 0 && newULBA != (lastBaseAddress >> 16) + 1)
+						return binSize;
+					lastBaseAddress = newULBA << 16;
+					in.skip(2 /* check sum */);
+					break;
+				}
+				case 0x02: {
+					// extended segment address record
+					final int newSBA = readAddress(in) << 4;
+					if (binSize > 0 && (newSBA >> 16) != (lastBaseAddress >> 16) + 1)
+						return binSize;
+					lastBaseAddress = newSBA;
+					in.skip(2 /* check sum */);
+					break;
+				}
+				case 0x00:
+					// data type line
+					lastAddress = lastBaseAddress + offset;
+					if (lastAddress >= mbrSize) // we must skip all data from below last MBR address (default 0x1000) as those are the MBR. The Soft Device starts at the end of MBR (0x1000), the app and bootloader farther more
+						binSize += lineSize;
+					// no break!
+				default:
+					in.skip(lineSize * 2 /* 2 hex per one byte */+ 2 /* check sum */);
+					break;
+				}
+				// skip end of line
+				while (true) {
+					b = in.read();
+
+					if (b != '\n' && b != '\r') {
+						break;
+					}
+				}
+			}
+		} finally {
+			in.reset();
+		}
+	}
+
+	@Override
+	public int available() {
+		return available - bytesRead;
+	}
+
+	/**
+	 * Fills the buffer with next bytes from the stream.
+	 * 
+	 * @return the size of the buffer
+	 * @throws java.io.IOException
+	 */
+	public int readPacket(byte[] buffer) throws HexFileValidationException, IOException {
+		int i = 0;
+		while (i < buffer.length) {
+			if (localPos < size) {
+				buffer[i++] = localBuf[localPos++];
+				continue;
+			}
+
+			bytesRead += size = readLine();
+			if (size == 0)
+				break; // end of file reached
+		}
+		return i;
+	}
+
+	@Override
+	public int read() throws IOException {
+		throw new UnsupportedOperationException("Please, use readPacket() method instead");
+	}
+
+	@Override
+	public int read(byte[] buffer) throws IOException {
+		return readPacket(buffer);
+	}
+
+	@Override
+	public int read(byte[] buffer, int offset, int count) throws IOException {
+		throw new UnsupportedOperationException("Please, use readPacket() method instead");
+	}
+
+	/**
+	 * Returns the total number of bytes.
+	 * 
+	 * @return total number of bytes available
+	 */
+	public int sizeInBytes() {
+		return available;
+	}
+
+	/**
+	 * Returns the total number of packets with given size that are needed to get all available data
+	 * 
+	 * @param packetSize
+	 *            the maximum packet size
+	 * @return the number of packets needed to get all the content
+	 * @throws java.io.IOException
+	 */
+	public int sizeInPackets(final int packetSize) throws IOException {
+		final int sizeInBytes = sizeInBytes();
+
+		return sizeInBytes / packetSize + ((sizeInBytes % packetSize) > 0 ? 1 : 0);
+	}
+
+	/**
+	 * Reads new line from the input stream. Input stream must be a HEX file. The first line is always skipped.
+	 * 
+	 * @return the number of data bytes in the new line. 0 if end of file.
+	 * @throws java.io.IOException
+	 *             if this stream is closed or another IOException occurs.
+	 */
+	private int readLine() throws IOException {
+		// end of file reached
+		if (pos == -1)
+			return 0;
+		final InputStream in = this.in;
+
+		// temporary value
+		int b;
+
+		int lineSize, type, offset;
+		do {
+			// skip end of line
+			while (true) {
+				b = in.read();
+				pos++;
+
+				if (b != '\n' && b != '\r') {
+					break;
+				}
+			}
+
+			/*
+			 * Each line starts with comma (':')
+			 * Data is written in HEX, so each 2 ASCII letters give one byte.
+			 * After the comma there is one byte (2 HEX signs) with line length (normally 10 -> 0x10 -> 16 bytes -> 32 HEX characters)
+			 * After that there is a 4 byte of an address. This part may be skipped.
+			 * There is a packet type after the address (1 byte = 2 HEX characters). 00 is the valid data. Other values can be skipped when
+			 * converting to BIN file.
+			 * Then goes n bytes of data followed by 1 byte (2 HEX chars) of checksum, which is also skipped in BIN file.
+			 */
+			checkComma(b); // checking the comma at the beginning
+			lineSize = readByte(in); // reading the length of the data in this line
+			pos += 2;
+			offset = readAddress(in);// reading the offset
+			pos += 4;
+			type = readByte(in); // reading the line type
+			pos += 2;
+
+			// if the line type is no longer data type (0x00), we've reached the end of the file
+			switch (type) {
+			case 0x00:
+				// data type
+				if (lastAddress + offset < MBRSize) { // skip MBR
+					type = -1; // some other than 0
+					pos += in.skip(lineSize * 2 /* 2 hex per one byte */+ 2 /* check sum */);
+				}
+				break;
+			case 0x01:
+				// end of file
+				pos = -1;
+				return 0;
+			case 0x02: {
+				// extended segment address
+				final int address = readAddress(in) << 4;
+				pos += 4;
+				if (bytesRead > 0 && (address >> 16) != (lastAddress >> 16) + 1)
+					return 0;
+				lastAddress = address;
+				pos += in.skip(2 /* check sum */);
+				break;
+			}
+			case 0x04: {
+				// extended linear address
+				final int address = readAddress(in);
+				pos += 4;
+				if (bytesRead > 0 && address != (lastAddress >> 16) + 1)
+					return 0;
+				lastAddress = address << 16;
+				pos += in.skip(2 /* check sum */);
+				break;
+			}
+			default:
+				pos += in.skip(lineSize * 2 /* 2 hex per one byte */+ 2 /* check sum */);
+				break;
+			}
+		} while (type != 0);
+
+		// otherwise read lineSize bytes or fill the whole buffer
+		for (int i = 0; i < localBuf.length && i < lineSize; ++i) {
+			b = readByte(in);
+			pos += 2;
+			localBuf[i] = (byte) b;
+		}
+		pos += in.skip(2); // skip the checksum
+		localPos = 0;
+
+		return lineSize;
+	}
+
+	@Override
+	public synchronized void reset() throws IOException {
+		super.reset();
+
+		pos = 0;
+		bytesRead = 0;
+		localPos = 0;
+	}
+
+	private void checkComma(final int comma) throws HexFileValidationException {
+		if (comma != ':')
+			throw new HexFileValidationException("Not a HEX file");
+	}
+
+	private int readByte(final InputStream in) throws IOException {
+		final int first = asciiToInt(in.read());
+		final int second = asciiToInt(in.read());
+
+		return first << 4 | second;
+	}
+
+	private int readAddress(final InputStream in) throws IOException {
+		return readByte(in) << 8 | readByte(in);
+	}
+
+	private int asciiToInt(final int ascii) {
+		if (ascii >= 'A')
+			return ascii - 0x37;
+
+		if (ascii >= '0')
+			return ascii - '0';
+		return -1;
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/DeviceDisconnectedException.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/DeviceDisconnectedException.java
new file mode 100644
index 0000000..4716ddf
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/DeviceDisconnectedException.java
@@ -0,0 +1,47 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.exception;
+
+/**
+ * Device has disconnected.
+ */
+public class DeviceDisconnectedException extends Exception {
+	private static final long serialVersionUID = -6901728550661937942L;
+
+	private final int mState;
+
+	public DeviceDisconnectedException(final String message, final int state) {
+		super(message);
+
+		mState = state;
+	}
+
+	public int getConnectionState() {
+		return mState;
+	}
+
+	@Override
+	public String getMessage() {
+		return super.getMessage() + " (connection state: " + mState + ")";
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/DfuException.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/DfuException.java
new file mode 100644
index 0000000..3e6cb65
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/DfuException.java
@@ -0,0 +1,49 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.exception;
+
+import no.nordicsemi.android.dfu.DfuBaseService;
+
+/**
+ * A DFU error occurred on the remote DFU target.
+ */
+public class DfuException extends Exception {
+	private static final long serialVersionUID = -6901728550661937942L;
+
+	private final int mError;
+
+	public DfuException(final String message, final int state) {
+		super(message);
+
+		mError = state;
+	}
+
+	public int getErrorNumber() {
+		return mError;
+	}
+
+	@Override
+	public String getMessage() {
+		return super.getMessage() + " (error " + (mError & ~DfuBaseService.ERROR_CONNECTION_MASK) + ")";
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/HexFileValidationException.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/HexFileValidationException.java
new file mode 100644
index 0000000..c6a872a
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/HexFileValidationException.java
@@ -0,0 +1,36 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.exception;
+
+import java.io.IOException;
+
+/**
+ * The HEX file could not be parsed.
+ */
+public class HexFileValidationException extends IOException {
+	private static final long serialVersionUID = -6467104024030837875L;
+
+	public HexFileValidationException(final String message) {
+		super(message);
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/RemoteDfuException.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/RemoteDfuException.java
new file mode 100644
index 0000000..5dc99c9
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/RemoteDfuException.java
@@ -0,0 +1,47 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.exception;
+
+/**
+ * A DFU error occurred on the remote DFU target.
+ */
+public class RemoteDfuException extends Exception {
+	private static final long serialVersionUID = -6901728550661937942L;
+
+	private final int mState;
+
+	public RemoteDfuException(final String message, final int state) {
+		super(message);
+
+		mState = state;
+	}
+
+	public int getErrorNumber() {
+		return mState;
+	}
+
+	@Override
+	public String getMessage() {
+		return super.getMessage() + " (error " + mState + ")";
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/SizeValidationException.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/SizeValidationException.java
new file mode 100644
index 0000000..037e91f
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/SizeValidationException.java
@@ -0,0 +1,37 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.exception;
+
+import java.io.IOException;
+
+/**
+ * This exception is thrown when the firmware size is not word-aligned (number of bytes does not divide by 4).
+ * This is the requirement for the DFU Bootloader.
+ */
+public class SizeValidationException extends IOException {
+	private static final long serialVersionUID = -6467104024030837875L;
+
+	public SizeValidationException(final String message) {
+		super(message);
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/UnknownResponseException.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/UnknownResponseException.java
new file mode 100644
index 0000000..5e19107
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/UnknownResponseException.java
@@ -0,0 +1,57 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.exception;
+
+public class UnknownResponseException extends Exception {
+	private static final long serialVersionUID = -8716125467309979289L;
+	private static final char[] HEX_ARRAY = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+	private final byte[] mResponse;
+	private final int mExpectedOpCode;
+
+	public UnknownResponseException(final String message, final byte[] response, final int expectedOpCode) {
+		super(message);
+
+		mResponse = response != null ? response : new byte[0];
+		mExpectedOpCode = expectedOpCode;
+	}
+
+	@Override
+	public String getMessage() {
+		return String.format("%s (response: %s, expected: 0x10%02X..)", super.getMessage(), bytesToHex(mResponse, 0, mResponse.length), mExpectedOpCode);
+	}
+
+	public static String bytesToHex(final byte[] bytes, final int start, final int length) {
+		if (bytes == null || bytes.length <= start || length <= 0)
+			return "";
+
+		final int maxLength = Math.min(length, bytes.length - start);
+		final char[] hexChars = new char[maxLength * 2];
+		for (int j = 0; j < maxLength; j++) {
+			final int v = bytes[start + j] & 0xFF;
+			hexChars[j * 2] = HEX_ARRAY[v >>> 4];
+			hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
+		}
+		return "0x" + new String(hexChars);
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/UploadAbortedException.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/UploadAbortedException.java
new file mode 100644
index 0000000..b563ad0
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/exception/UploadAbortedException.java
@@ -0,0 +1,31 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.exception;
+
+public class UploadAbortedException extends Exception {
+	private static final long serialVersionUID = -6901728550661937942L;
+
+	public UploadAbortedException() {
+		super();
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/FileInfo.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/FileInfo.java
new file mode 100644
index 0000000..6dae4fd
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/FileInfo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package no.nordicsemi.android.dfu.internal.manifest;
+
+import com.google.gson.annotations.SerializedName;
+
+public class FileInfo {
+	@SerializedName("bin_file") protected String binFile;
+	@SerializedName("dat_file") protected String datFile;
+	@SerializedName("init_packet_data") protected InitPacketData initPacketData;
+
+	public String getBinFileName() {
+		return binFile;
+	}
+
+	public String getDatFileName() {
+		return datFile;
+	}
+
+	public InitPacketData getInitPacketData() {
+		return initPacketData;
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/InitPacketData.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/InitPacketData.java
new file mode 100644
index 0000000..541a744
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/InitPacketData.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package no.nordicsemi.android.dfu.internal.manifest;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+public class InitPacketData {
+	@SerializedName("packet_version") protected int packetVersion;
+	@SerializedName("compression_type") protected int compressionType;
+	@SerializedName("application_version") protected long applicationVersion;
+	@SerializedName("device_revision") protected int deviceRevision;
+	@SerializedName("device_type") protected int deviceType;
+	@SerializedName("firmware_crc16") protected int firmwareCRC16;
+	@SerializedName("firmware_hash") protected String firmwareHash;
+	@SerializedName("softdevice_req") protected List<Integer> softdeviceReq;
+
+	public int getPacketVersion() {
+		return packetVersion;
+	}
+
+	public int getCompressionType() {
+		return compressionType;
+	}
+
+	public long getApplicationVersion() {
+		return applicationVersion;
+	}
+
+	public int getDeviceRevision() {
+		return deviceRevision;
+	}
+
+	public int getDeviceType() {
+		return deviceType;
+	}
+
+	public int getFirmwareCRC16() {
+		return firmwareCRC16;
+	}
+
+	public String getFirmwareHash() {
+		return firmwareHash;
+	}
+
+	public List<Integer> getSoftdeviceReq() {
+		return softdeviceReq;
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/Manifest.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/Manifest.java
new file mode 100644
index 0000000..f2c9ab1
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/Manifest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package no.nordicsemi.android.dfu.internal.manifest;
+
+import com.google.gson.annotations.SerializedName;
+
+public class Manifest {
+	protected FileInfo application;
+	protected FileInfo bootloader;
+	protected FileInfo softdevice;
+	@SerializedName("softdevice_bootloader") protected SoftDeviceBootloaderFileInfo softdeviceBootloader;
+
+	public FileInfo getApplicationInfo() {
+		return application;
+	}
+
+	public FileInfo getBootloaderInfo() {
+		return bootloader;
+	}
+
+	public FileInfo getSoftdeviceInfo() {
+		return softdevice;
+	}
+
+	public SoftDeviceBootloaderFileInfo getSoftdeviceBootloaderInfo() {
+		return softdeviceBootloader;
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/ManifestFile.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/ManifestFile.java
new file mode 100644
index 0000000..1463c61
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/ManifestFile.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package no.nordicsemi.android.dfu.internal.manifest;
+
+public class ManifestFile {
+	protected Manifest manifest;
+
+	public Manifest getManifest() {
+		return manifest;
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/SoftDeviceBootloaderFileInfo.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/SoftDeviceBootloaderFileInfo.java
new file mode 100644
index 0000000..5f413bd
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/manifest/SoftDeviceBootloaderFileInfo.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package no.nordicsemi.android.dfu.internal.manifest;
+
+import com.google.gson.annotations.SerializedName;
+
+public class SoftDeviceBootloaderFileInfo extends FileInfo {
+	@SerializedName("bl_size") protected int bootloaderSize;
+	@SerializedName("sd_size") protected int softdeviceSize;
+
+	public int getSoftdeviceSize() {
+		return softdeviceSize;
+	}
+
+	public int getBootloaderSize() {
+		return bootloaderSize;
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScanner.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScanner.java
new file mode 100644
index 0000000..bcab414
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScanner.java
@@ -0,0 +1,54 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.scanner;
+
+/**
+ * <p>
+ *     The DFU Bootloader may advertise with the same address as an application (in case of the buttonless update) or one incremented by 1 (in case of jumping to the DFU mode with a button,
+ *     or after flashing the new Soft Device (flashing new SD removes the old application)).
+ * </p>
+ * <p>
+ *     The DFU service always connects to the address given as a parameter. However, when flashing SD+BL+App it will first send the SD+BL as part one followed by the App in the second connection.
+ *     As the service does not know which address was used in the first connection (normal, when buttonless update, or +1 when with-button update) we have to scan for the advertising device
+ *     after SD+BL part is completed.
+ * </p>
+ */
+public interface BootloaderScanner {
+	/**
+	 * After the buttonless jump from the application mode to the bootloader mode the service will wait this long for the advertising bootloader (in milliseconds).
+	 */
+	public final static long TIMEOUT = 5000l; // ms
+	/** The bootloader may advertise with the same address or one with the last byte incremented by this value. F.e. 00:11:22:33:44:55 -> 00:11:22:33:44:56. FF changes to 00. */
+	public final static int ADDRESS_DIFF = 1;
+
+	/**
+	 * Searches for the advertising bootloader. The bootloader may advertise with the same device address or one with the last byte incremented by 1.
+	 * This method is a blocking one and ends when such device is found. There are two implementations of this interface - one for Androids 4.3 and 4.4.x and one for
+	 * the Android 5+ devices.
+	 *
+	 * @param deviceAddress
+	 *            the application device address
+	 * @return the address of the advertising DFU bootloader. If may be the same as the application address or one with the last byte incremented by 1 (AA:BB:CC:DD:EE:45/FF -> AA:BB:CC:DD:EE:46/00).
+	 */
+	public String searchFor(final String deviceAddress);
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerFactory.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerFactory.java
new file mode 100644
index 0000000..8596497
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerFactory.java
@@ -0,0 +1,42 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.scanner;
+
+import android.os.Build;
+
+/**
+ * The factory should be used to create the {@link BootloaderScanner} instance appropriate for the Android version.
+ */
+public class BootloaderScannerFactory {
+
+	/**
+	 * Returns the scanner implementation.
+	 *
+	 * @return the bootloader scanner
+	 */
+	public static BootloaderScanner getScanner() {
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+			return new BootloaderScannerLollipop();
+		return new BootloaderScannerJB();
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerJB.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerJB.java
new file mode 100644
index 0000000..77c4a37
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerJB.java
@@ -0,0 +1,104 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.scanner;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * @see BootloaderScanner
+ */
+public class BootloaderScannerJB implements BootloaderScanner, BluetoothAdapter.LeScanCallback {
+	private final Object mLock = new Object();
+	private String mDeviceAddress;
+	private String mDeviceAddressIncremented;
+	private String mBootloaderAddress;
+	private boolean mFound;
+
+	@SuppressWarnings("deprecation")
+	@Override
+	public String searchFor(final String deviceAddress) {
+		final String firstBytes = deviceAddress.substring(0, 15);
+		final String lastByte = deviceAddress.substring(15); // assuming that the device address is correct
+		final String lastByteIncremented = String.format("%02X", (Integer.valueOf(lastByte, 16) + ADDRESS_DIFF) & 0xFF);
+
+		mDeviceAddress = deviceAddress;
+		mDeviceAddressIncremented = firstBytes + lastByteIncremented;
+		mBootloaderAddress = null;
+		mFound = false;
+
+		// Add timeout
+		new Thread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					Thread.sleep(BootloaderScanner.TIMEOUT);
+				} catch (final InterruptedException e) {
+					// do nothing
+				}
+
+				if (mFound)
+					return;
+
+				mBootloaderAddress = null;
+				mFound = true;
+
+				// Notify the waiting thread
+				synchronized (mLock) {
+					mLock.notifyAll();
+				}
+			}
+		}, "Scanner timer").start();
+
+		final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+		adapter.startLeScan(this);
+
+		try {
+			synchronized (mLock) {
+				while (!mFound)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			// do nothing
+		}
+
+		adapter.stopLeScan(this);
+		return mBootloaderAddress;
+	}
+
+	@Override
+	public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
+		final String address = device.getAddress();
+
+		if (mDeviceAddress.equals(address) || mDeviceAddressIncremented.equals(address)) {
+			mBootloaderAddress = address;
+			mFound = true;
+
+			// Notify the waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+	}
+
+}
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerLollipop.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerLollipop.java
new file mode 100644
index 0000000..ef26d48
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerLollipop.java
@@ -0,0 +1,117 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.dfu.internal.scanner;
+
+import android.annotation.TargetApi;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.os.Build;
+
+/**
+ * @see BootloaderScanner
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class BootloaderScannerLollipop extends ScanCallback implements BootloaderScanner {
+	private final Object mLock = new Object();
+	private String mDeviceAddress;
+	private String mDeviceAddressIncremented;
+	private String mBootloaderAddress;
+	private boolean mFound;
+
+	@Override
+	public String searchFor(final String deviceAddress) {
+		final String firstBytes = deviceAddress.substring(0, 15);
+		final String lastByte = deviceAddress.substring(15); // assuming that the device address is correct
+		final String lastByteIncremented = String.format("%02X", (Integer.valueOf(lastByte, 16) + ADDRESS_DIFF) & 0xFF);
+
+		mDeviceAddress = deviceAddress;
+		mDeviceAddressIncremented = firstBytes + lastByteIncremented;
+		mBootloaderAddress = null;
+		mFound = false;
+
+		// Add timeout
+		new Thread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					Thread.sleep(BootloaderScanner.TIMEOUT);
+				} catch (final InterruptedException e) {
+					// do nothing
+				}
+
+				if (mFound)
+					return;
+
+				mBootloaderAddress = null;
+				mFound = true;
+
+				// Notify the waiting thread
+				synchronized (mLock) {
+					mLock.notifyAll();
+				}
+			}
+		}, "Scanner timer").start();
+
+		final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+		final BluetoothLeScanner scanner = adapter.getBluetoothLeScanner();
+		/*
+		 * Scanning with filters does not work on Nexus 9 (Android 5.1). No devices are found and scanner terminates on timeout.
+		 * We will match the device address in the callback method instead. It's not like it should be, but at least it works.
+		 */
+		//final List<ScanFilter> filters = new ArrayList<>();
+		//filters.add(new ScanFilter.Builder().setDeviceAddress(mDeviceAddress).build());
+		//filters.add(new ScanFilter.Builder().setDeviceAddress(mDeviceAddressIncremented).build());
+		final ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+		scanner.startScan(/*filters*/ null, settings, this);
+
+		try {
+			synchronized (mLock) {
+				while (!mFound)
+					mLock.wait();
+			}
+		} catch (final InterruptedException e) {
+			// do nothing
+		}
+
+		scanner.stopScan(this);
+		return mBootloaderAddress;
+	}
+
+	@Override
+	public void onScanResult(final int callbackType, final ScanResult result) {
+		final String address = result.getDevice().getAddress();
+
+		if (mDeviceAddress.equals(address) || mDeviceAddressIncremented.equals(address)) {
+			mBootloaderAddress = address;
+			mFound = true;
+
+			// Notify the waiting thread
+			synchronized (mLock) {
+				mLock.notifyAll();
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/error/GattError.java b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/error/GattError.java
new file mode 100644
index 0000000..defb40d
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/dfu/src/no/nordicsemi/android/error/GattError.java
@@ -0,0 +1,190 @@
+/*************************************************************************************************************************************************
+ * Copyright (c) 2015, Nordic Semiconductor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ************************************************************************************************************************************************/
+
+package no.nordicsemi.android.error;
+
+import android.bluetooth.BluetoothGatt;
+
+import no.nordicsemi.android.dfu.DfuBaseService;
+
+/**
+ * Parses the error numbers according to the <b>gatt_api.h</b> file from bluedroid stack.
+ * See: https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h (and other versions) for details.
+ */
+public class GattError {
+
+	// Starts at line 106 of gatt_api.h file
+	/**
+	 * Converts the connection status given by the {@link android.bluetooth.BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} to error name.
+	 * @param error the status number
+	 * @return the error name as stated in the gatt_api.h file
+	 */
+	public static String parseConnectionError(final int error) {
+		switch (error) {
+			case BluetoothGatt.GATT_SUCCESS:
+				return "SUCCESS";
+			case 0x01:
+				return "GATT CONN L2C FAILURE";
+			case 0x08:
+				return "GATT CONN TIMEOUT";
+			case 0x13:
+				return "GATT CONN TERMINATE PEER USER";
+			case 0x16:
+				return "GATT CONN TERMINATE LOCAL HOST";
+			case 0x3E:
+				return "GATT CONN FAIL ESTABLISH";
+			case 0x22:
+				return "GATT CONN LMP TIMEOUT";
+			case 0x0100:
+				return "GATT CONN CANCEL ";
+			case 0x0085:
+				return "GATT ERROR"; // Device not reachable
+			default:
+				return "UNKNOWN (" + error + ")";
+		}
+	}
+
+	// Starts at line 29 of the gatt_api.h file
+	/**
+	 * Converts the bluetooth communication status given by other BluetoothGattCallbacks to error name. It also parses the DFU errors.
+	 * @param error the status number
+	 * @return the error name as stated in the gatt_api.h file
+	 */
+	public static String parse(final int error) {
+		switch (error) {
+			case 0x0001:
+				return "GATT INVALID HANDLE";
+			case 0x0002:
+				return "GATT READ NOT PERMIT";
+			case 0x0003:
+				return "GATT WRITE NOT PERMIT";
+			case 0x0004:
+				return "GATT INVALID PDU";
+			case 0x0005:
+				return "GATT INSUF AUTHENTICATION";
+			case 0x0006:
+				return "GATT REQ NOT SUPPORTED";
+			case 0x0007:
+				return "GATT INVALID OFFSET";
+			case 0x0008:
+				return "GATT INSUF AUTHORIZATION";
+			case 0x0009:
+				return "GATT PREPARE Q FULL";
+			case 0x000a:
+				return "GATT NOT FOUND";
+			case 0x000b:
+				return "GATT NOT LONG";
+			case 0x000c:
+				return "GATT INSUF KEY SIZE";
+			case 0x000d:
+				return "GATT INVALID ATTR LEN";
+			case 0x000e:
+				return "GATT ERR UNLIKELY";
+			case 0x000f:
+				return "GATT INSUF ENCRYPTION";
+			case 0x0010:
+				return "GATT UNSUPPORT GRP TYPE";
+			case 0x0011:
+				return "GATT INSUF RESOURCE";
+			case 0x0087:
+				return "GATT ILLEGAL PARAMETER";
+			case 0x0080:
+				return "GATT NO RESOURCES";
+			case 0x0081:
+				return "GATT INTERNAL ERROR";
+			case 0x0082:
+				return "GATT WRONG STATE";
+			case 0x0083:
+				return "GATT DB FULL";
+			case 0x0084:
+				return "GATT BUSY";
+			case 0x0085:
+				return "GATT ERROR";
+			case 0x0086:
+				return "GATT CMD STARTED";
+			case 0x0088:
+				return "GATT PENDING";
+			case 0x0089:
+				return "GATT AUTH FAIL";
+			case 0x008a:
+				return "GATT MORE";
+			case 0x008b:
+				return "GATT INVALID CFG";
+			case 0x008c:
+				return "GATT SERVICE STARTED";
+			case 0x008d:
+				return "GATT ENCRYPTED NO MITM";
+			case 0x008e:
+				return "GATT NOT ENCRYPTED";
+			case 0x008f:
+				return "GATT CONGESTED";
+			case 0x00FD:
+				return "GATT CCCD CFG ERROR";
+			case 0x00FE:
+				return "GATT PROCEDURE IN PROGRESS";
+			case 0x00FF:
+				return "GATT VALUE OUT OF RANGE";
+			case 0x0101:
+				return "TOO MANY OPEN CONNECTIONS";
+			case DfuBaseService.ERROR_DEVICE_DISCONNECTED:
+				return "DFU DEVICE DISCONNECTED";
+			case DfuBaseService.ERROR_FILE_ERROR:
+				return "DFU FILE ERROR";
+			case DfuBaseService.ERROR_FILE_INVALID:
+				return "DFU NOT A VALID HEX FILE";
+			case DfuBaseService.ERROR_FILE_SIZE_INVALID:
+				return "DFU FILE NOT WORD ALIGNED";
+			case DfuBaseService.ERROR_FILE_IO_EXCEPTION:
+				return "DFU IO EXCEPTION";
+			case DfuBaseService.ERROR_FILE_NOT_FOUND:
+				return "DFU FILE NOT FOUND";
+			case DfuBaseService.ERROR_SERVICE_DISCOVERY_NOT_STARTED:
+				return "DFU SERVICE DISCOVERY NOT STARTED";
+			case DfuBaseService.ERROR_SERVICE_NOT_FOUND:
+				return "DFU SERVICE NOT FOUND";
+			case DfuBaseService.ERROR_CHARACTERISTICS_NOT_FOUND:
+				return "DFU CHARACTERISTICS NOT FOUND";
+			case DfuBaseService.ERROR_FILE_TYPE_UNSUPPORTED:
+				return "DFU FILE TYPE NOT SUPPORTED";
+			case DfuBaseService.ERROR_BLUETOOTH_DISABLED:
+				return "BLUETOOTH ADAPTER DISABLED";
+			case DfuBaseService.ERROR_INIT_PACKET_REQUIRED:
+				return "INIT PACKET REQUIRED";
+			default:
+				if ((DfuBaseService.ERROR_REMOTE_MASK & error) > 0) {
+					switch (error & (~DfuBaseService.ERROR_REMOTE_MASK)) {
+						case DfuBaseService.DFU_STATUS_INVALID_STATE:
+							return "REMOTE DFU INVALID STATE";
+						case DfuBaseService.DFU_STATUS_NOT_SUPPORTED:
+							return "REMOTE DFU NOT SUPPORTED";
+						case DfuBaseService.DFU_STATUS_DATA_SIZE_EXCEEDS_LIMIT:
+							return "REMOTE DFU DATA SIZE EXCEEDS LIMIT";
+						case DfuBaseService.DFU_STATUS_CRC_ERROR:
+							return "REMOTE DFU INVALID CRC ERROR";
+						case DfuBaseService.DFU_STATUS_OPERATION_FAILED:
+							return "REMOTE DFU OPERATION FAILED";
+					}
+				}
+				return "UNKNOWN (" + error + ")";
+		}
+	}
+}
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/Android.mk b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/Android.mk
new file mode 100644
index 0000000..691f812
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := 23
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
+    $(LOCAL_PATH)/../dfu/res
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_STATIC_JAVA_LIBRARIES := dfu-library
+LOCAL_PACKAGE_NAME := DragonKeyboardFirmwareUpdater
+LOCAL_AAPT_FLAGS := --auto-add-overlay
+LOCAL_AAPT_FLAGS += --extra-packages no.nordicsemi.android.dfu
+
+include $(BUILD_PACKAGE)
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/AndroidManifest.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/AndroidManifest.xml
new file mode 100644
index 0000000..6bc885b
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/AndroidManifest.xml
@@ -0,0 +1,56 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.dragonkeyboardfirmwareupdater" >
+
+    <uses-feature
+        android:name="android.hardware.bluetooth_le"
+        android:required="true" />
+
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <application
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:persistent="true"
+        android:theme="@style/AppTheme">
+
+        <receiver android:name=".BootCompletedBroadcastReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+
+        <service android:name=".KeyboardFirmwareUpdateService"
+                 android:exported="true" />
+
+        <service android:name=".DfuService"
+                 android:exported="false" />
+
+        <activity android:name=".UpdateConfirmationActivity"
+                  android:excludeFromRecents="true" />
+
+        <activity android:name=".NotificationActivity"
+                  android:exported="true"
+                  android:excludeFromRecents="true" />
+    </application>
+
+</manifest>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-hdpi/bg_keyboard_header.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-hdpi/bg_keyboard_header.png
new file mode 100644
index 0000000..5e16a03
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-hdpi/bg_keyboard_header.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-mdpi/bg_keyboard_header.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-mdpi/bg_keyboard_header.png
new file mode 100644
index 0000000..dc9bc7f
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-mdpi/bg_keyboard_header.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xhdpi/bg_keyboard_header.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xhdpi/bg_keyboard_header.png
new file mode 100644
index 0000000..a83040c
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xhdpi/bg_keyboard_header.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xxhdpi/bg_keyboard_header.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xxhdpi/bg_keyboard_header.png
new file mode 100644
index 0000000..d064532
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xxhdpi/bg_keyboard_header.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xxxhdpi/bg_keyboard_header.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xxxhdpi/bg_keyboard_header.png
new file mode 100644
index 0000000..d0e6a44
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable-xxxhdpi/bg_keyboard_header.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/bg_keyboard_header.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/bg_keyboard_header.png
new file mode 100644
index 0000000..d0e6a44
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/bg_keyboard_header.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_install.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_install.xml
new file mode 100644
index 0000000..a44e403
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_install.xml
@@ -0,0 +1,27 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
+</vector>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_keyboard.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_keyboard.xml
new file mode 100644
index 0000000..0d79c1b
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_keyboard.xml
@@ -0,0 +1,30 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M20 5H4c-1.1 0-1.99 .9 -1.99 2L2 17c0 1.1 .9 2 2 2h16c1.1 0 2-.9
+2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0
+3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9
+7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z" />
+    <path
+        android:pathData="M0 0h24v24H0zm0 0h24v24H0z" />
+</vector>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_later.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_later.xml
new file mode 100644
index 0000000..a2d9304
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_later.xml
@@ -0,0 +1,33 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:fillAlpha=".9"
+        android:pathData="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2
+11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:fillAlpha=".9"
+        android:pathData="M12.5 7H11v6l5.25 3.15 .75 -1.23-4.5-2.67z" />
+</vector>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/layout/activity_update_confirmation.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/layout/activity_update_confirmation.xml
new file mode 100644
index 0000000..6348bde
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/layout/activity_update_confirmation.xml
@@ -0,0 +1,45 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="16dp"
+    android:paddingRight="16dp"
+    android:orientation="vertical"
+    tools:context="com.android.dragonkeyboardfirmwareupdater.UpdateConfirmationActivity">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:layout_margin="15dp">
+        <TextView
+            android:id="@+id/confirmation_update_info"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/confirmation_update_info"
+            android:textSize="15sp"/>
+        <Button
+            android:id="@+id/confirmation_install_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|end"
+            android:layout_margin="5dp"
+            android:textSize="15sp"
+            android:text="@string/confirmation_install"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-hdpi/ic_launcher.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-mdpi/ic_launcher.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xhdpi/ic_launcher.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xxhdpi/ic_launcher.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xxxhdpi/ic_launcher.png b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/raw/image_151104_mp_signed.hex b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/raw/image_151104_mp_signed.hex
new file mode 100644
index 0000000..59189de
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/raw/image_151104_mp_signed.hex
@@ -0,0 +1,4099 @@
+:020000021000EC

+:10900000004000209D130200051402000714020016

+:109010000000000000000000000000000000000050

+:109020000000000000000000000000000914020021

+:1090300000000000000000000B1402000D140200EC

+:10904000090F02000F1402000F1402000F14020097

+:109050000F14020000000000B9E501000F14020027

+:109060000F1402000F1402000F1402000F1402006C

+:109070000F1402000F1402000F1402000F1402005C

+:109080000F140200AD9301000F1402000F14020030

+:109090006D9401000F140200F9E201000F140200A8

+:1090A0000F1402000F140200000000000000000076

+:1090B00000000000000000000000000000000000B0

+:1090C0000090010036DF704737DF704710B503248A

+:1090D0000723144010D1094B1A604B1CDB009BB2D4

+:1090E000D218074B1A60074B1C70074B1C70074BBC

+:1090F0001880074B1980231C181C10BD2C20002041

+:10910000302000203720002036200020342000208E

+:1091100038200020F0B54A4B87B002921A8801909F

+:109120000E1C039309208A4200D287E004ABDF1DA6

+:1091300000233B70434B04255B68DB05DB0D21D02E

+:10914000103BD9B24AB2002A0DDA0F220A40083A7F

+:109150003D4B92089200D21855680322114091406D

+:109160002B1CCB4009E09208C03238499200555878

+:10917000032213409340DD402B1CDBB29B09DDB280

+:10918000012B0DD0381CFFF79DFF022801D172B6CC

+:1091900007E0002805D000210A1C01F0EFF900E0EB

+:1091A0001D1C2B4B2B4A19782B4B12881C78C9B2EB

+:1091B000E4B20020944201D20134E0B288420CD0E3

+:1091C0001C781B780021DBB2934201D20133D9B263

+:1091D000214B1970012D03D112E0012D2DD01F4C10

+:1091E00062B63878FFF770FF0223021C9A4303D05F

+:1091F00000210A1C01F0C2F9184B04209C421DD02A

+:10920000174F029A3B68E5005B191A60019A002A21

+:1092100010D0002E0ED0039B111C1B88321C5C4307

+:10922000104B1868001908F07DF93B6800205D19A3

+:10923000AE8003E00020988000E0042007B0F0BD7D

+:109240003420002000ED00E018ED00E000E100E037

+:10925000362000203820002037200020FFFF0000AB

+:109260002C2000203020002038B5104C104D2378E1

+:109270002A789A4219D00F49237822780888D2B2E6

+:10928000DBB20021904201D90132D1B20A4A2170E9

+:1092900010880A4A58431268DB001018084A1268FE

+:1092A000D31899881B689847E1E738BD362000201D

+:1092B00037200020382000203420002030200020DB

+:1092C0002C200020174B02205968C905C90D27D052

+:1092D0001039CBB25AB2002A0DDA0F221A40083ADE

+:1092E000C24082401049521803210B408B40526803

+:1092F000DA40131C09E0032092080C4BC0329200A4

+:10930000D25801408140131CCB40DBB29B09DBB239

+:109310000020012B04D0033B5A4253410220C01AC3

+:109320007047C04600ED00E018ED00E000E100E00D

+:1093300080221A4B9202DA67D2218022184B520205

+:1093400089005A50C22189005A5001212F225960A8

+:10935000101C0138C046C046C046C046C046C04684

+:10936000C046C046C046C046C046C046F1D199601E

+:1093700000230C49101C0B600138C046C046C04693

+:10938000C046C046C046C046C046C046C046C046AD

+:10939000C046F1D1044A13707047C04604E100E0B2

+:1093A000001001403C20002064200020F8B500247B

+:1093B000234B5C649C64DC641C655C609C60214B9A

+:1093C0001E68731C3BD0204A204B214FD358214AA2

+:1093D00015685D1B2D022D0A24235E433B689B19F3

+:1093E0005A68954217D3AD1AA4181B4A1E6A126810

+:1093F000D96994469A696346002B08D0101CE0474F

+:10940000002806D000210A1C01F0B8F801E0081C71

+:109410009047731CE0D1114B114A197813788B4295

+:1094200005D10133DBB2022B00D100231370137876

+:109430000C4A9B009C5080220B4B52035A60F8BD93

+:10944000FC100140542000200010014004050000E1

+:10945000442000203C2000204020002050200020FC

+:10946000512000205C200020FCE100E0F0B5BD4A66

+:10947000BD491368BD488BB0029300780B789842C1

+:1094800011D00133DBB2022B00D100230B700B781B

+:10949000B7499B005B589C46029B63441B021B0A16

+:1094A0001360012301E000239C460493B14B1B6829

+:1094B0000593B14B1B780193B04B1B680993B04BDC

+:1094C0001F681368019A0893AE4B00921B780393B0

+:1094D000059B00990139CAB20092FF2A5FD0009A19

+:1094E000D100099A51180C784A78A242F1D018227A

+:1094F00062438D7801344868E4B22D1B8218681EDF

+:10950000854110786D422C40022803D01D1C032891

+:1095100043D139E0242052681D1C5043069238186C

+:10952000027D0792002A38D01A1C6E1C08D0069EB5

+:10953000B54207D024266E43BE192A1C356AF4E7CB

+:109540001D1C1EE0AA4200D083E124256B43FB18BA

+:109550001D6A6B1C06D101268B4B9E60079B0393F3

+:1095600000230893242672434368BA180693036ABB

+:1095700013625A1C05D07343FB185A68069E96194D

+:109580005E600023037508E06B1C06D024235D4356

+:1095900000237D192B752D6AF6E72B1CA4E769467D

+:1095A000784A097B734C11706E4A089923601160E8

+:1095B000049A002A09D1059A9B1A5A1E9341DBB2DC

+:1095C000029301235B42009330E000206C4A242781

+:1095D0001268051C03920122524200925A1C21D0AB

+:1095E0003A1C5A4303998A1851688C4504D2236067

+:1095F0006346C91A516017E06646761AB4464018A9

+:109600001669116A556015750491002E08D00299EB

+:10961000D66041180902090A91600099009311620D

+:10962000049BDBE7534A136001230293514B1B68F1

+:109630000393524B039A1B680693514B1B689C463D

+:10964000484B1B6805934F4B1B780793019F013FC5

+:10965000FBB20193FF2B00D19AE0019BD900069B3E

+:109660005918131C009A013207D024220098424353

+:109670006244041C106A009026E00A784878904200

+:109680006ED0182050434C68013220188C78D2B22A

+:109690000A70944201D100220A70027844681E1CAC

+:1096A000012A5BD1242262436244157D002D55D1ED

+:1096B00086689660C668D660066916614069D061A2

+:1096C0000798002800D095609068059D2F4F451B96

+:1096D0002D022D0AD668BD4202D8AD19556009E0A9

+:1096E000059D281A0002000A864201D9301A00E0BE

+:1096F0000020506000209060D0600120242510756B

+:1097000001206543404210626544261C5A1C25D046

+:1097100024225A43624468685668B04204D8301A1A

+:109720005060261C2B6219E01A1C04932426564311

+:1097300066447768B84206D9366AC01B0492771C23

+:1097400005D0321CF2E73F1A7760161C049A686055

+:1097500024202E621E1C424362441462331C81E7A3

+:109760001A1C73E73C2000205020002051200020CC

+:109770005C20002054200020482000204C200020A5

+:10978000442000205820002000100140FEFF7F00F0

+:10979000314B1A60039B9A4202D1029B002B53D09B

+:1097A000531C4FD024212D4B4A4318682C49821852

+:1097B0002C4B2D481B680C5855682C4EE21A12028F

+:1097C000120A3778033200920190002F20D18020B6

+:1097D000D1274002BF00C851C127BF00C851C022D5

+:1097E00080272348BF0252008750076001272F209F

+:1097F0000F600138C046C046C046C046C046C0469D

+:10980000C046C046C046C046C046C046F1D13770CB

+:10981000009AAA4200D22A1CD318A8221B021B0AB3

+:10982000D2008B50019A1B1B89581B02091B09028D

+:10983000090A03311B0A994206D980220D4B920274

+:109840005A6001E0FFF774FD00220B4B1A700BB059

+:10985000F0BD1D1C86E6C046542000204420002098

+:10986000001001403C20002004050000642000207E

+:1098700000E100E0FCE100E058200020F7B50325FE

+:1098800001900F1C161C1C1C1D400720002D50D1E0

+:10989000002B4ED0FFF74CFD264B089A1A60264B42

+:1098A0001F70264B1C6024235F43E7193B1CA74213

+:1098B00003D0257025752434F9E70321204A11705F

+:1098C000204A00211760182218373C1C72435F6041

+:1098D000197059709E7008330020BF18A342F6D14A

+:1098E0000122194B52421A60184B80211870184BF4

+:1098F000C5241870C023FF25164A49035B00D150C8

+:10990000A4001359AB43C0252B431351A12311606D

+:10991000019C1149DB00CC50C424A40015590F4B05

+:109920002B40C0252D022B4313510D4BCA580D4B14

+:109930001A60FEBD4020002052200020442000205C

+:10994000482000204C2000205420002050200020DF

+:109950005120002000E100E000100140FF00FFFF67

+:10996000040500003C200020F0B5104B061C1B68CD

+:109970000820002B19D00720002A16D0002E14D062

+:1099800000240B480778BC420EDA1D1C2433181C37

+:1099900024380078002805D1012369702B70AA6152

+:1099A000346002E00134EEE70420F0BD44200020E2

+:1099B00052200020F7B5244B051C18680C1C082108

+:1099C00000283FD0214B07211B789D423AD2042C1E

+:1099D00038D924236B43C31818780821012831D1C2

+:1099E0005B780027012B00D1271C161C0194FFF780

+:1099F00069FC174BC0001A68121853789178581CEC

+:109A0000411A4C1EA1414942084011788C4604215C

+:109A1000604517D018214B435168CB180421002B07

+:109A200010D001215D6019700A4D0B4969581F6102

+:109A3000996001995E61D960507080220021074BC6

+:109A400052035A60081CFEBD442000205220002012

+:109A50004C2000200010014004050000FCE100E063

+:109A600038B51A4B041C1A680820002A2CD0184B51

+:109A700007201B789C4227D2242363439B5C082049

+:109A8000012B21D1FFF71EFC124BC0001B681B18D5

+:109A90005D789A78691C8A1A501E824152421140A0

+:109AA0001A78042091420FD018226A43586882180D

+:109AB0000420002A08D00220546010708022002068

+:109AC0005970054B52035A6038BDC04644200020EF

+:109AD000522000204C200020FCE100E010B5041CC6

+:109AE000082904D00020011C021C00F047FD60681A

+:109AF0002368984710BD000007B5044A0090019103

+:109B000068460821FFF706FB0EBDC046DD9A01003E

+:109B10008023C0220349DB05D20099500021024A6C

+:109B200099507047FFFFFF000406000029DF7047CF

+:109B300028DF704737B5021C081C8B680D781C01A4

+:109B40000B6949880830009102496458291CA047D4

+:109B500037BDC046FC210020F0B55C4C151C1A1C1A

+:109B6000237885B0071C002B06D12B1C134301D191

+:109B7000022300E00123237023780320013B052BFF

+:109B800000D9A0E0181C01920E1C07F08DFC031EEA

+:109B90002D6552868023E8205B0519691D691F69C0

+:109BA0001B6989B28002019307F032FC6B469A88E8

+:109BB0000138ADB2B9B2684371439208FFF7B6FFFE

+:109BC0000223002800D17DE07DE0081CFFF7B0FFF4

+:109BD000002878D1002D2AD1019B002B23D13B787E

+:109BE000042B1ED107231FE08022E82052051369B1

+:109BF000116912699BB289B280025E43029207F03A

+:109C000007FC6B461B89411E5943301C2A1CFFF779

+:109C10008DFF002857D1019B002B04D13B78042BEA

+:109C200018D0052300E00423237000204BE0032319

+:109C300048E080235B051B697A889BB25E43A800DD

+:109C4000801939699208FFF771FF00283BD1019B09

+:109C50001D43C7D00623E7E78022E8205205136999

+:109C600079889BB202915E431169029B126989B2A5

+:109C70008002F618039207F0CBFB6B469B890138F4

+:109C80005843AD00029B76192D18E918301C019A33

+:109C9000FFF74CFF0028C1D015E08023E8205B05CA

+:109CA0001969800289B21D691E6907F0B1FBADB266

+:109CB0000138B1B2684307F0ABFBFFF739FF00286A

+:109CC00001D10723237005B0F0BDC0461522002046

+:109CD000F7B514243C4B1A7854431C190092221CEB

+:109CE00026790832271D95680193042E18D0052E79

+:109CF0004AD00F20022E66D1344BE288188863695F

+:109D0000800219182389121A5D19314B28189A42BA

+:109D100001D8920801E080225200FFF707FF4DE0D2

+:109D2000802651682B4A0B01D31818895A895B6821

+:109D30007605AB4204D1131C4343E288934201D021

+:109D400002290AD13169281C89B207F089FB1F4B0F

+:109D50001B88C018FFF7ECFE30E03169281C89B27F

+:109D600007F07EFB14233269366992B24243011C2C

+:109D7000B6B2013046430098AA1A434301989208AC

+:109D8000C318DB88EB1814E0802676053169281C9F

+:109D900089B207F065FB3269366992B24243238982

+:109DA000011CB6B201305B194643E0889A1A92084A

+:109DB000C318F31A9B08381CFFF7CEFE002802D107

+:109DC0000123019A9370FEBD682000201622002016

+:109DD000FF030000FC210020F8B5144D061C6C7830

+:109DE00006A807880420142C1FD028782418E4B271

+:109DF000132C01D9143CE4B2142044432C196261A1

+:109E00000020221C267160710C3241C941C2E380DE

+:109E10002781AB78002B01D0002003E0FFF758FF2B

+:109E20001128F9D06B7801336B70F8BD68200020E1

+:109E3000F7B548498B780C1C002B00D189E0002530

+:109E4000454B8D701A78082A07D1022800D0434B61

+:109E50001D706378002B4BD17BE0022802D00328D1

+:109E600052D076E00E7814213D4871430388611882

+:109E700001339BB203800F790190022F04D1CA886D

+:109E80009B02934210DA2DE0052F5BD0042F29D1DD

+:109E9000C988D71F784278418C46071C9B02291C31

+:109EA000634547DA002F4AD000232B4A1370019AEA

+:109EB00013806378013B6370731CDBB2132B01D9F1

+:109EC000331C133B237014235E43A619311D00205D

+:109ED000FFF730FE002301213371F380F16033611D

+:109EE000736133816378002B33D0A378002B30D19A

+:109EF000FFF7EEFE00282CD011282AD01423217859

+:109F000059436118043112E014230A780D20534399

+:109F1000CB185A791C1D0132D2B25A71142A05D0BD

+:109F2000FFF7D6FE002814D0112812D0211CFFF70D

+:109F300001FE0EE00121002AB4D0291CB2E700295D

+:109F4000D0D0B1E7D11F4A4251412F1C0029ABD1DB

+:109F5000A8E7F7BD68200020152200201422002069

+:109F600016220020F7B500230120254A84461380DD

+:109F70001A1C244C23706370A370A11D6546881EB3

+:109F80000270E0180A80C560026142610281C82047

+:109F9000143300260127143140008342EED180257E

+:109FA000E820194B6D051E8029692B6989B2800252

+:109FB000019307F02DFA6B469B880238584308221C

+:109FC000124B1860E820124B80021E601E819E8199

+:109FD0005E81104B1A7029692E6989B207F018FA50

+:109FE0002D690138B6B2A9B2704307F011FAFFF734

+:109FF0009FFD002802D1084BA7701F70FEBDC04610

+:10A0000016220020682000200C22002010220020B0

+:10A01000FC2100201522002014220020F0B5404B26

+:10A0200087B01B780824002B76D00E24002873D02C

+:10A03000002971D00368002B6ED0802783887F05AC

+:10A0400000933E690724B6B2B34265D80F2B63D99B

+:10A05000C388002B60D0334E051C3268E820019283

+:10A06000009A03915A439446029239693B6989B236

+:10A070000493019B80026344059307F0C9F96B4682

+:10A080001B8A01385843059B834245D83B69029A95

+:10A090009BB29A4207DD3869009980B207F0E0F977

+:10A0A00089B2002938D10323009A07241A40131ECD

+:10A0B00032D11D48042402880090012A2CD0039933

+:10A0C00001980A60486019491401019F0819476006

+:10A0D0002F688C4667508021AF8849050781ED883D

+:10A0E00045817D4330680F690133BFB29BB2BD42E9

+:10A0F00003D90F69BFB2ED1B00E0002580277F0563

+:10A100003F69BFB2C019002DEDD16444A3812C1C5E

+:10A11000009B013230601A80201C07B0F0BDC046A1

+:10A1200014220020102200200C220020FC210020FC

+:10A1300030B5104B1C780823002C19D00E230028B2

+:10A1400016D0002A14D00368002B10D10A4D2C68B9

+:10A15000002C0CD02C89406861430918688944435D

+:10A1600068682418A14202D21360516000E00723FE

+:10A17000181C30BD14220020FC210020F7B5041C5F

+:10A18000194805780820002D2DD00E2000292AD04E

+:10A19000002C28D025680720002D24D1134D2E68CF

+:10A1A000002E20D066686F89B4462E896D6877438B

+:10A1B0007F19BC4517D2002A15D0B24213D8D51842

+:10A1C000B54210DC0325102029420CD12B420AD1C4

+:10A1D00066462E4207D11D1C0220131C00950A1C46

+:10A1E000211CFFF7F9FDFEBD14220020FC210020F8

+:10A1F000F7B5041C194805780820002D2DD00E2035

+:10A2000000292AD0002C28D025680720002D24D131

+:10A21000134D2E68002E20D066686F89B4462E89B3

+:10A220006D6877437F19BC4517D2002A15D0B2421A

+:10A2300013D8D518B54210DC0325102029420CD1C3

+:10A240002B420AD166462E4207D11D1C0520131C45

+:10A2500000950A1C211CFFF7BFFDFEBD1422002043

+:10A26000FC210020F0B50D1C111C244A85B0127889

+:10A2700002930824002A3DD00E24002D3AD0002855

+:10A2800038D02C68002C32D11D4E3268002A2ED0D6

+:10A29000378973896A687B439C4673689C4462452E

+:10A2A00025D2002923D0B94221D8029BCB18BB422A

+:10A2B0001DDC03239C4618421BD1029F1F4218D16C

+:10A2C0006746131C3B401F1E13D1029B0391991834

+:10A2D000039A029007F026F9039A2B6800921B015B

+:10A2E000F658281C03213A1C029BB04702E00724C1

+:10A2F00000E01024201C05B0F0BDC0461422002050

+:10A30000FC21002073B5154B041C1B780D1C082084

+:10A31000002B21D00E20002C1ED023680720002BFC

+:10A320001AD10F4A1368002B16D05668118952892A

+:10A3300063684A43921993420ED210209A070BD1B8

+:10A34000981B07F08DF807200A1E05D10091042004

+:10A35000211C2B1CFFF740FD76BDC04614220020B7

+:10A36000FC210020064B1A780823002A06D00E2371

+:10A37000002803D0034B5B7803600023181C704750

+:10A38000142200206820002077DF704747DF7047E5

+:10A3900011DF704713DF704713B50322041C5049C7

+:10A3A00001A807F0BFF84F4B002C00D189E0002234

+:10A3B0001A70692C48D014D8612C2ED008D8422CA1

+:10A3C00075D1494806F040FF012006F005FF74E012

+:10A3D000622C2FD0682C6AD1444806F035FF4448DF

+:10A3E00069E06C2C50D002D86B2C3CD05FE0722C12

+:10A3F00056D0762C5BD13F4806F026FF3E4BD868FE

+:10A4000006F022FF3D4806F01FFF3D4806F01CFF06

+:10A410003C4806F019FF3C484DE03C4806F014FF6C

+:10A420003B4B186806F028FF2E2006F0F5FE394B4E

+:10A43000186815E004F09EF9041C374806F004FF84

+:10A44000201C06F029FF38E080243448640506F01B

+:10A45000FBFE606E06F010FF2E2006F0DDFE206E83

+:10A4600006F00AFF29E02E4806F0EEFE2D4C237878

+:10A47000002B01D02C4800E02C4806F0E5FE01221C

+:10A4800023785340237018E0294806F0DDFE06F0DB

+:10A49000E5FB002802D1274806F0D6FE26480AE050

+:10A4A00080220121254BD205D150254803E0201CF4

+:10A4B00006F0B2FE234806F0C7FE01A806F0C4FE6F

+:10A4C00013BD1A78002A01D1012202E0012A01D12C

+:10A4D00002221A701B78022BEFD104F079FFECE70F

+:10A4E000591A02000D27002082170200B51702003A

+:10A4F000F6170200B41802001C320020BF18020038

+:10A50000CA180200D6180200DB1802006017020009

+:10A51000A8000010A4000010681702003E180200F6

+:10A5200044180200EC2700205718020062180200AD

+:10A530006C1802007318020078180200440500002D

+:10A5400080180200E418020070B50A4E04AC2578A9

+:10A55000367805AC2478AE420AD1074D2D78A54255

+:10A5600006D10C1C111C1A1C06F0DCFF0123237001

+:10A5700070BDC046F4270020F827002070B5061CE7

+:10A58000141C0D1C114806F05FFE301C06F074FE12

+:10A590000F4806F059FE0F4806F056FE201C06F044

+:10A5A00053FE0D4806F050FE281C06F075FE0B4BBE

+:10A5B0001E600B4B1D600B4B1C60BFF34F8F0A4A94

+:10A5C0000A4BDA60BFF34F8FFEE7C046EF18020078

+:10A5D000591A02001C19020020190200183200202A

+:10A5E00054320020103200200400FA0500ED00E093

+:10A5F00013B5144C2378002B0AD0134B1868FFF7BF

+:10A600002FFA002803D000210A1CFFF7B7FF2070A3

+:10A6100000220E4B191CD8331A600D4BD4311B7815

+:10A620000A60012B0AD10B4B08211B786846009267

+:10A63000019204F0FBFC04F0ABFE01E004F0C8FE64

+:10A6400013BDC04608280020F02700201027002056

+:10A650000C270020F827002038B5FFF799FE184D89

+:10A66000041E286002D000210A1C08E0E8208002B5

+:10A67000FFF790FE2860002803D0211C221CFFF762

+:10A680007DFF8021C0220F4BC90352009950012148

+:10A690001A680D4C0B1C83401A4200D0E36701304E

+:10A6A0002028F7D18022B121084BD205D150BFF329

+:10A6B0004F8F074A074BDA60BFF34F8FFEE7C04664

+:10A6C0001432002000E100E004E100E01C0500007D

+:10A6D0000400FA0500ED00E0F7B50025324B0095C7

+:10A6E0001B780195AB425DD0304C231C221CD43327

+:10A6F000D8321B6812689A420ED12D4C2378AB4297

+:10A7000050D02C4B1868FFF7ABF9A84202D0291C97

+:10A710002A1C36E0207045E00A22694653431A5D40

+:10A720000A70E218D1798D4207D2281CEA18A218C3

+:10A730005278684482700135F5E71F4B68461A78F5

+:10A740001E4B08211B7804F071FC1D4E051E1CD108

+:10A75000271CD4373B681421581C06F081FED834DE

+:10A760002368013139608B421BD1114C2378002BB7

+:10A7700017D0104B1868FFF773F9051E03D000219E

+:10A780000A1CFFF7FBFE20700BE03368984208D0EC

+:10A790000C4806F059FD281C06F06EFD0A4806F02C

+:10A7A00053FD3560F7BDC0460C2700201027002060

+:10A7B00008280020F0270020F4270020F827002098

+:10A7C000FC27002024190200591A020010B5104B72

+:10A7D0001B78022B1AD00478002C09D000200D4AD7

+:10A7E000011CFEF797FC002810D000210A1C0BE08A

+:10A7F0004378002B0AD0201C211C074AFEF78AFC54

+:10A80000002803D0211C221CFFF7B8FE10BDC04653

+:10A810000D270020D9100200FD100200F8B5037AC0

+:10A82000041C002B1ED0C379042B1BD1FFF7E0FEC4

+:10A830002378212B04D1514806F006FDFFF70CFFC9

+:10A840004F4D504C022B05D14F4806F0FDFC012323

+:10A850002B7005E04D4806F0F7FC01232B70002318

+:10A86000237088E0657A002D05D0494806F0ECFC9D

+:10A87000FFF7BEFE7FE0474B1B78002B22D0E37929

+:10A88000012B78D16278082A06D1434A1378002B2D

+:10A8900071D0013B13706EE00A2A0AD13F4806F0DE

+:10A8A000D3FC3F4804F0B8FB002864D0291C2A1CC4

+:10A8B0005DE039490B78052B5DD8581C0870384984

+:10A8C000CA5458E0374B1B78002B03D12E2006F0DA

+:10A8D000A3FC1EE0344806F0B7FC207806F0CCFC60

+:10A8E000324806F0B1FCE07906F0C6FC304806F0CC

+:10A8F000ABFC251CE3792A1B01359A4206DA20209D

+:10A9000006F08AFC287806F0B7FCF3E7234806F047

+:10A910009BFC284F14213E1CD8363568681C06F075

+:10A920009FFD3B1CD4331B680131994203D122485F

+:10A9300006F08AFC1FE00A225543211C781906F014

+:10A94000F1FD33681421581C06F08AFD1B4D0131BE

+:10A950002C783160002C0ED1F621194B4900186873

+:10A96000221CFFF727F8002803D0211C221CFFF728

+:10A9700005FE01232B7006F0EBFBF8BD2719020042

+:10A98000F5270020FB270020331902003A190200A6

+:10A9900041190200F6270020F7270020591A02006B

+:10A9A00000280020EC27002053190200651902003E

+:10A9B00071190200102700208519020008280020C4

+:10A9C000F027002008B5031C0A1C0020191CFFF703

+:10A9D000D5FD0000F8B52B4C061C00213822201CA8

+:10A9E00006F0E2FD284B01222360284B002563601E

+:10A9F000274B6283E360274BA5602383264B2561A9

+:10AA0000A3820223A375E1182377244BCA77A3847A

+:10AA10000223FF33E385231C192130331D70231CCF

+:10AA20006185D22132331D70231CA185032131336E

+:10AA30001970A2184B21D5770622194BE2841970A0

+:10AA40004221102259709D70164B2285E577A5771B

+:10AA5000657765631868144F391C06F06FFE134B59

+:10AA600013486363211C231DDE77276104F0BEF9C0

+:10AA7000104B1860A84203D0291C2A1CFFF77EFD4A

+:10AA8000F8BDC0461C3200208A1902009B19020042

+:10AA9000A219020018500000D1180000C1030000E4

+:10AAA00000200020A40000101822002049A5010069

+:10AAB000B9AA010014320020F7B5041C00780D2853

+:10AAC00000D93CE106F0FAFC0E006400DF006F00E4

+:10AAD000D40098002B01AB00AD00AF00C500CC0046

+:10AAE0003401E8009B4F9C484C2101223B1C04F0A0

+:10AAF00039FC041E00D0B0E00823984E38780121BC

+:10AB000000930196221C231C04F030FC051E18D172

+:10AB1000012338780093F318019302212A1C2B1C7F

+:10AB200004F024FC041E00D0D2E002257619387807

+:10AB3000032100950196221C231C04F017FC002819

+:10AB400003D0211C221CFFF719FD03F013FE041C87

+:10AB500004F0A6FA031C061CAB4308D0804806F09C

+:10AB600073FB301C06F098FB7E4806F06DFB7E48B8

+:10AB700006F06AFB201C06F08FFB7C4806F064FBA5

+:10AB8000012004F095FA002867D1FFF7FDFBDFE014

+:10AB9000774806F059FB96200122764BC0001A70C8

+:10ABA00006F07EFAD4E06379032B04D1724806F0F4

+:10ABB0004BFBA068C7E0022B00D0C9E06F4806F04D

+:10ABC00043FBA06806F058FB6D4806F03DFBA36808

+:10ABD000187806F051FB6B4806F036FBA07906F0BA

+:10ABE0004BFB694806F030FBE07906F045FB67480F

+:10ABF00006F02AFB2079A6E0654806F025FBA08830

+:10AC000006F04AFB634806F01FFBE08806F044FBB1

+:10AC1000614806F019FB208906F03EFB95E05F488D

+:10AC200094E05F4892E0302106225E4806F0BCFCCA

+:10AC3000002005F067FE01225B4B5C481A70002281

+:10AC40005B4B1A705B4B191CD833D4310A601A6005

+:10AC50007CE0594806F0F8FA584800210A1C72E7CF

+:10AC6000012005F04FFE00224F4B55481A706DE051

+:10AC7000544806F0E9FA207906F0FEFA524806F048

+:10AC8000E3FA60795FE0514806F0DEFA0022394BC2

+:10AC90001A7004F09DFB5BE04D4806F0D5FA4D4C70

+:10ACA0002578002D0AD000254B4806F0CDFA2570F6

+:10ACB0004A4C2078FFF78EFE257049E0484B1B7800

+:10ACC000022B24D1FFF764FB464E041E306003D0F4

+:10ACD000201C291C2A1C36E7E8208002FFF75AFBBB

+:10ACE0003060002800D02CE78021C0223E4BC903F1

+:10ACF0005200995001211A683C4C0B1C83401A42A7

+:10AD000000D0E36701302028F7D105F067FD38480F

+:10AD100006F09AFA0020011C06F0F0F918E0354818

+:10AD200006F092FA04F054FB00222A4B1A700FE04E

+:10AD3000314806F089FAFFF75BFCFFF78DFC2F48DE

+:10AD400006F082FA207806F097FA084806F07CFAB6

+:10AD5000F7BDC046F4270020201B0200F827002082

+:10AD6000AB190200B2190200BE190200591A020002

+:10AD7000D31902000C270020D9190200E2190200A1

+:10AD8000EB190200F2190200FA190200001A02007F

+:10AD9000061A02001D1A02002E1A0200451A0200AD

+:10ADA0004C1A020000280020F62700205C1A02003E

+:10ADB000F727002010270020721A0200ADDE0000E5

+:10ADC000801A0200931A0200831902009D1A0200E1

+:10ADD000A71A0200F5270020B21A0200FB27002064

+:10ADE0000D2700201432002000E100E004E100E023

+:10ADF000C41A0200D01A0200DA1A0200E61A02008F

+:10AE0000F0B58FB0FEF784FE8027AF230126DB006C

+:10AE1000FF05FE50A02303214C4ADB0599504C4A04

+:10AE20000C2099504B4A9950E7220221D2009950A8

+:10AE30008021A1220903D20099501421464AFEF72D

+:10AE400045F9051E02D000210A1C0BE0434B0B21E3

+:10AE500000930A22424BFEF711FD041E04D0201C71

+:10AE6000291C2A1CFFF78AFB301CFFF78FFA3D4D87

+:10AE7000211C281CDC2206F097FB2B1C3A48D433FB

+:10AE8000D835311C394A2C601C60FEF76DFD051E5B

+:10AE90003CD10A2003F0D2FCFF208024021C1821A0

+:10AEA0001C23009506F008FAE400314806F0CCF9BE

+:10AEB000385906F0E1F92F4806F0C6F93B59301C25

+:10AEC0003B512D4905F070FE041EC8D105F024FD4C

+:10AED0002A4806F0B9F9201CFFF77CFD284806F047

+:10AEE000B3F9284B03AD6B60274B281CAB600223E2

+:10AEF0002B709623DB002B6203236B6220232349F4

+:10AF00002C76AB6206F060F8002802D0211C221CCF

+:10AF1000A8E77620FFF740FA6120FFF73DFA6220AC

+:10AF2000FFF73AFA6C20FFF737FA194CFEF79CF955

+:10AF3000281C06F057F9002805D02878FFF72CFACE

+:10AF4000201C06F081F906F0B5F8EFE74C07000089

+:10AF500034070000440700000C280020F99A010083

+:10AF60002422002010270020F0270020D9A601006D

+:10AF7000EB1A0200591A02001DA80100FD1A020076

+:10AF80000A1B0200650C020031F60100CDA701008A

+:10AF9000151B020072DF704779DF70477DDF704755

+:10AFA00065DF7047F0B50024061C251C87B00C9F98

+:10AFB00000933B7803910292019333889D423ADAE1

+:10AFC0007368A9005918042205A806F0ABFA04AB6F

+:10AFD00005A8D91C0022FFF7E3FF002835D104ABF8

+:10AFE000DB78029A934222D16242624102993B7815

+:10AFF000520059188A181F2A1BDC002C07D16946F9

+:10B000009A1C3A700133009A097BDBB2D15404AB2D

+:10B01000DC1C3A78009B05A89A18211CFFF7C0FF9A

+:10B02000002812D1237801243A78D3183B700135D7

+:10B03000C3E70C2009E00020844206D03B78019A47

+:10B04000013B9B1A0199009A535407B0F0BDF7B524

+:10B05000089E171C00960222051C1C1CFFF7A2FF6D

+:10B06000002806D10096281C391C1022231CFFF74B

+:10B0700099FFFEBD0023F0B51370037889B0061C5C

+:10B080000D1C141C002B03D1B378002B4BD029E0EE

+:10B0900043780C201D2B00DD50E10E2704ABFF1878

+:10B0A0001D23881C391C3B80FFF778FF002800D047

+:10B0B00044E13378022B03D13B8809221D2B08D9A8

+:10B0C0007378002B03D09BB208221D2B01D90822D4

+:10B0D0003B88DBB2591C29706A70227802329B18B7

+:10B0E0002370D1E723780C201B2B00DD26E10E27EF

+:10B0F00004ABFF18381CFFF74FFF002800D01DE1FC

+:10B1000023785A1C22700322EA5423785A1C227096

+:10B110001922EA5423783A88EB181A70120A5A70E6

+:10B12000237802332370F378002B0ED023785A1C37

+:10B1300022700222EA5423785A1C22700122EA5417

+:10B1400023789A182270F278EA547368002B12D090

+:10B150001A7823780C201C2B00DDEFE0591C21709D

+:10B160000221E9542378591C21700A21E9542378DB

+:10B17000591C2170EA543389002B03D1338A002BE8

+:10B1800016D00AE0301C00940830022106222B1C45

+:10B19000FFF75DFF0028F1D0D0E0301C00941030A4

+:10B1A000032107222B1CFFF752FF002800D0C5E027

+:10B1B000338B002B0AD0301C009418301421152238

+:10B1C0002B1CFFF744FF002800D0B7E0336A002BA8

+:10B1D00049D022780C20049206321F2A00D9ADE013

+:10B1E00019880720052900D8A8E0554A554F8A1824

+:10B1F00092B2BA4200D8A1E05A880392052A00D838

+:10B200009CE04F4FBC469444674602AA97819789B9

+:10B21000BC464C4FBC4500D890E04B4FB94205D0DE

+:10B22000039ABA4202D0914200D987E0049A049965

+:10B230000132227005226A542278511C2170122199

+:10B24000A95422781988AA181170090A5170227815

+:10B250000232D2B222705B88AA1813701B0A537094

+:10B26000237802332370776A002F29D03A79217826

+:10B27000931C881CDBB2C3180C201F2B5EDC4B1CFC

+:10B28000033223706A5423785A1C2270FF22EA5436

+:10B2900023783A88EB181A70120A5A70237802330E

+:10B2A000DBB22370BA88002A0AD0B96807200029C7

+:10B2B00044D0E81806F036F9BA882378D3182370FA

+:10B2C000331C2C3305931B78002B01D1002035E073

+:10B2D000B36A002B31D000230493059B049A1B789A

+:10B2E0009342F3D90C27049B22785F43B36A511C25

+:10B2F000DF193B7921700333AB5423785A1C227039

+:10B300001622EA5423783A88EB181A70120A5A70F7

+:10B3100020780230C0B22070BA88002A09D0B968FB

+:10B3200000290AD0281806F0FDF8BA882378D31827

+:10B330002370049B0133DBB2CEE7072009B0F0BDD8

+:10B340007FF3FFFF7DF30000FFFF0000F0B593B037

+:10B350006B466A469E1D0023D71D0D1C33703B7043

+:10B36000984204D0C3785B070FD407201DE0041C6B

+:10B37000002D03D0EB78002BF7D10EE02A1C31789A

+:10B380003B78201CFFF706FE0FE002A9321CFFF7F6

+:10B3900071FE02AC0028EBD007E03A1C281C0AA979

+:10B3A000FFF768FE0AAA0028E9D013B0F0BDA0DFBD

+:10B3B0007047A2DF7047A3DF7047A4DF7047A6DFA6

+:10B3C000704713B50B88112B05D0502B07D0102BCD

+:10B3D0001ED18B8801E001235B42438218E0037D8C

+:10B3E000002B15D0CC884289944211D1CB8B022BF3

+:10B3F0000ED10268002A0BD08B1CDB7F0131C97F84

+:10B400001B020B43012101AC99432170211C904781

+:10B4100013BD0000F0B50B680124036001235B42FB

+:10B4200043820A7993B003745B4B0275071C021DBB

+:10B430000D1C201C04A98C700B80FFF7B8FF011CA9

+:10B44000061E00D0A4E03B7D002B0FD002AE0322ED

+:10B45000301C06F0A9F8102306221C4334706B7BC5

+:10B460007370B378934302221343B3700BAE301C56

+:10B4700000211C2206F098F80220327801210243B4

+:10B4800010203B7D8243194009010A433270591E46

+:10B490008B4100225B427260F260326102AA1A4064

+:10B4A00000237261B3616A46012393753B4B00210F

+:10B4B000938203A8032206F077F86A46AB7B06ACBA

+:10B4C0001373EB7B00215373937B06229343022279

+:10B4D00013430822934310229343012293436A4665

+:10B4E00093732B7B201CD370142206F05DF805AB00

+:10B4F000069303AB6360012323810023638101234F

+:10B50000A3816B4603332361311CB888BB1D221C09

+:10B51000FFF74FFF061E3BD1AB68002B37D06A46C2

+:10B52000012393751E4B311C938203A8032206F05E

+:10B530003BF86A462B7CA968137300235373937BF3

+:10B54000062293430222134308229343102293437B

+:10B55000012293436A4601A8937301F099FB051CED

+:10B56000311C1422201C06F01FF83A1C05AB236086

+:10B5700003AB6360ADB201AB66812581A581236118

+:10B58000F8880E32211CFFF716FF061C00E0F88138

+:10B59000301C13B0F0BDC0460F180000192A00007F

+:10B5A00008290000F0B589B06B46D971DF1D037C16

+:10B5B000051C002499422ED002AE211C0822301C0A

+:10B5C00005F0F2FF012333803B78E9882B74688A09

+:10B5D000321C74807760FFF7F0FE041C011E1AD144

+:10B5E0006A8A0E4B08249A4215D02B7D002B12D06C

+:10B5F00004AC201C102205F0D7FFEB88211C23800F

+:10B600000123A3707388A660A3807368E360688ACF

+:10B61000FFF7D5FE041C201C09B0F0BDFFFF0000A1

+:10B6200075DF704776DF70477ADF70477BDF7047E2

+:10B63000F7B52E4B18882E4B984256D02D4A2E4CDB

+:10B6400013780133DBB21370217B99422DD32B4940

+:10B65000FFF7E6FF051C2A4F2A4E002D45D0A369AF

+:10B66000002B42D0112D09D1274805F0EDFD2748C8

+:10B670000021274AFEF778F9051CEEE7381C05F093

+:10B68000E3FD1C4B187805F0F7FD301C05F0DCFDE0

+:10B69000204805F0D9FD281C05F0EEFD301C05F012

+:10B6A000D3FD281CA3699847D7E700231370237C98

+:10B6B000002B13D03B21FFF7B5FF051E0ED0A36969

+:10B6C000002B0BD0144805F0BFFD281C05F0D4FD5D

+:10B6D0000C4805F0B9FD281CA36998476369002B45

+:10B6E00003D0002201A802709847F7BDDC29002092

+:10B6F000FFFF0000BA290020BC290020DE2900201D

+:10B70000831B0200591A02006C1B0200D82900207A

+:10B7100031B60100931B0200A91B020037B5184B7C

+:10B72000184A5B881188184C184D99420CD85288D9

+:10B730009A4209D36369002B03D0012201A8027049

+:10B74000984700232B7037BD2B78002B05D06369F9

+:10B75000002BF6D001A80022F1E70D4B1B78002B3F

+:10B7600001D1616800E0A1680A4B00221868FEF769

+:10B7700021F90028E5D0A369002BE1D1E1E7C0461B

+:10B78000B2290020DE290020BC290020B029002099

+:10B79000BA290020D8290020144938B5021C0B1CF6

+:10B7A00032CA32C332CA32C31268114C1A60002244

+:10B7B000104B01681A70201C914213D0082205F02A

+:10B7C000B1FE201CFFF730FF00280ED1012200211E

+:10B7D000094B52421A80094B094A0A481970FEF770

+:10B7E000C3F802E0FFF722FFEEE738BDBC290020D6

+:10B7F000DE290020B0290020DC290020BA29002001

+:10B8000031B60100D829002008B5024B1868FEF7B0

+:10B8100027F908BDD829002010B50388112B19D0AD

+:10B8200002D8102B05D043E0122B3AD0502B21D058

+:10B830003EE0011C82881F4B16311A801E480822E8

+:10B8400005F070FE00221D4B1A701D4BDB899342E0

+:10B850002ED12BE00122174B52421A800022174BA7

+:10B860001A70184B1868FEF7FBF8002820D0144B0C

+:10B870009B6912E0124CC188E289914218D1C38BB6

+:10B88000022B15D10130C37FDB070FD40D4B186895

+:10B89000FEF7E6F800280BD0A369002B08D09847E4

+:10B8A00006E0811D0822044805F03CFEFFF736FF44

+:10B8B00010BDC046DC290020B2290020BA29002092

+:10B8C000BC290020D829002037B5144C011C0822BF

+:10B8D000201C05F027FE201CFFF7A6FE211C051EDC

+:10B8E0001AD10F4B22885B889A4202D862889A420A

+:10B8F0000AD201240B4B1C700B4B1888FFF790FEEB

+:10B900000A4B051C1C7007E0094B5B69002B03D038

+:10B91000012201A802709847281C3EBDDE290020A4

+:10B92000B2290020B0290020DC290020BA290020FB

+:10B93000BC290020A0DF7047A2DF7047A5DF704759

+:10B94000A6DF7047A8DF704763DF7047F0B50E24AD

+:10B9500099B0002800D10DE1002900D10AE14B681F

+:10B96000002B00D106E101235B420380029101908C

+:10B970008249102208A805F0D5FD0A21804B06ADAA

+:10B980002B8004AB08A8C918FFF7DEFF041E00D007

+:10B99000F0E0019B01201A1D291CFFF7CBFF041EBC

+:10B9A00000D0E7E0AB78019A11AD9371211C1C2205

+:10B9B000281C05F0F9FD04222B786C6013432B70D2

+:10B9C000019BEC602C616C61AC619B7904AA937360

+:10B9D0006C4B05AF9381211C0322381C05F0E4FD5C

+:10B9E000002301223B701023134306227B70BB7897

+:10B9F0000CAE934302221343082293431022934335

+:10BA00000122211C1343301C1422BB7005F0CCFD15

+:10BA100007AB0C931423B381019B3481748134618F

+:10BA200077609888291C321C0833FFF785FF041EB5

+:10BA300000D09FE0211C1C22281C05F0B5FD082227

+:10BA40002B786C601343102213432B70019BEC6026

+:10BA50002C616C61AC619B7904AA93734A4B211CE5

+:10BA60009381381C032205F09FFD00230F223B70B9

+:10BA70007B78211C9343012213430F221340102291

+:10BA8000134306227B70BB78301C9343022213437E

+:10BA90000822934310221343012213431422BB7044

+:10BAA00005F082FD07AB0C931723B381019B348112

+:10BAB0007481346177609888291C321C1033FFF739

+:10BAC0003BFF041E56D1211C1C22281C05F06CFDD6

+:10BAD00002222B786C6013432B70019BEC602C616D

+:10BAE0006C61AC619B7904AA9373284B211C9381F0

+:10BAF000381C032205F058FD0F223B78211C93438C

+:10BB0000012213430F221340102213433B700023E2

+:10BB100006227B70BB78301C934302221343082219

+:10BB2000934310229343012213431422BB7005F068

+:10BB30003BFD07AB336002233381B381029B7481E9

+:10BB40003361019B77609888291C321C2033FFF7F2

+:10BB5000F3FE041E0ED1029B5B680393039A019BC4

+:10BB60009A62029B9B68002B01D0019AD36201224A

+:10BB7000074B1A70201C19B0F0BDC046C01B020054

+:10BB8000301500003215000031150000341500009A

+:10BB9000E6290020F0B5051C87B00E20002D23D02B

+:10BBA0002C88124B08209C421ED0114B1B78002B76

+:10BBB0001AD0104E1023717069468F1D032102ACFC

+:10BBC0003980B27000211A1C201C337005F0ECFC87

+:10BBD0002B8A288823800123A3700023211CA380A3

+:10BBE000A760E660FFF7ACFE07B0F0BDFFFF000006

+:10BBF000E6290020E8290020F0B589B0002800D10E

+:10BC0000B0E0002900D1ADE0826A002A00D1A9E0AD

+:10BC10000B88112B1AD004D8102B00D0A2E08B88EF

+:10BC200016E00D1C041C502B02D0512B12D099E0B1

+:10BC3000C9880389994200D094E0082304A90B70B5

+:10BC4000EB8B20350B718D6090478BE001235B42BD

+:10BC5000038087E08A79022A00D083E00A89038A78

+:10BC60009A4200D07EE08B7A043B022B00D879E028

+:10BC7000022301AE0021082204AF301C3B7005F006

+:10BC800093FC02233380002373806B46736020880B

+:10BC9000A18A321CFFF752FE002804D0E36A002B71

+:10BCA00005D0984703E06B461B78DB0707D4FE23DB

+:10BCB0002088FF33391CBB80FFF744FE4CE0B8807E

+:10BCC000391C2088FFF73EFE002845D1EB1CD97FA8

+:10BCD000481E07283CD805F0E7FB040E1B1D1F215A

+:10BCE0003423002333702B8C012B02D8201C01211C

+:10BCF00008E0012308E0012333702A8C9A4203D81C

+:10BD0000201C0221062225E023353371B5601AE09C

+:10BD1000022317E0032315E0042313E0052311E0B9

+:10BD20002B8C022B02D8201C082112E06B1DDB7F1C

+:10BD30000435EA7F1B021343B3805A425341063352

+:10BD400000E009233370A36A201C311C984709E0E6

+:10BD5000201C0322FFF71EFF002803D0E36A002BFC

+:10BD600000D0984709B0F0BDA0DF7047A2DF704750

+:10BD7000F0B593B003900191171C0293002903D1F1

+:10BD8000081C0A1CFEF7FAFB002F04D1381C391CD2

+:10BD90003A1CFEF7F3FB0BAC201C00211C2205F023

+:10BDA00003FC02222378002513436A460126928968

+:10BDB000237004AB9A80291C181C03229E716560B5

+:10BDC000E56025616561A56105F0EEFB029B04AAB3

+:10BDD0001B78291C1370029B5B78537093780622A2

+:10BDE000934302221343E7221340B34304AA06AE4F

+:10BDF0009370301C142205F0D7FB05AB069304ABFF

+:10BE00007360019B211C3361054B321C1888189B01

+:10BE100037817581B781FFF7A9FF13B0F0BDC04628

+:10BE2000382A002010B5041C012086B0544B03A909

+:10BE3000544A88700B80FFF797FF002800D09DE0E0

+:10BE40002288002A03D12289002A16D00AE04E4B0C

+:10BE50004E480093231C61683C33FFF789FF00289C

+:10BE6000F1D08BE04A4B4B480093231CE1683C33F4

+:10BE7000FFF77EFF002800D080E0228A002A09D048

+:10BE8000454B46480093231C61693C33FFF770FF24

+:10BE9000002873D1228B002A09D0414B41480093DE

+:10BEA000231CE1693C33FFF763FF002866D1228C35

+:10BEB000002A09D03C4B3D480093231C616A3C3367

+:10BEC000FFF756FF002859D1228D002A09D0384BA0

+:10BED00038480093231CE16A3C33FFF749FF0028F0

+:10BEE0004CD1236B002B1AD01A7804A90A705A7807

+:10BEF00031484A709A788A70DA78CA701A790A7169

+:10BF00009B681A0A4B711B0CCB712C4B8A710093E6

+:10BF1000231C08223C33FFF72BFF00282ED1636B34

+:10BF2000002B03D1A06B00280CD127E019681A79E7

+:10BF3000234B24480093231C3C33FFF719FF0028B0

+:10BF4000F0D01BE0037804A90B7043881E4A4B70A5

+:10BF50001B0A8B708388CB701B0A0B71C3881B482C

+:10BF60004B711B0A8B71231C00923C330722FFF795

+:10BF7000FFFE031C0020834200D0181C06B010BD39

+:10BF80000A180000382A0020082A0020292A000068

+:10BF9000F8290020242A0000282A0020252A000051

+:10BFA000202A0020272A0000302A0020262A00000C

+:10BFB000F0290020282A0000232A0000002A00205F

+:10BFC000102A00202A2A0000182A0020502A0000E7

+:10BFD000A0DF7047A1DF7047A2DF7047A3DF704783

+:10BFE000A4DF7047A6DF704765DF7047F0B50768CC

+:10BFF00087B00C1C002F10D0111C0422061C01ADB0

+:10C0000002A82A7005F08EFAA38B301C2B81E38BDB

+:10C0100020346B81EC60291CB84707B0F0BD0000EC

+:10C02000F0B5022793B00BAC0E1C01900021201C30

+:10C030001C2205F0B9FA237800253B432370012325

+:10C0400002AA9372204B291C138103A80322656066

+:10C05000E56025616561A56105F0A6FA331C3433FE

+:10C060001B7802AA1371331C35331B783036537199

+:10C0700006239279291C9A43E72317431F40012383

+:10C080009F4302AB9F71338805AF3B701B0A7B70E7

+:10C09000B3781422BB70F37806AEFB70301C05F049

+:10C0A00083FA04AB069303AB736004233381B3813B

+:10C0B000019B7581376118896733FF33211C321C5E

+:10C0C000FFF78AFF13B0F0BD4A2A000070B5138B4A

+:10C0D00086B0022B11D10568002D0ED00123032656

+:10C0E000041C927E02A81340F31A042201AE33709E

+:10C0F00005F018FA201C311CA84706B070BD0000DE

+:10C10000F0B5102695B002931AAB019204CB0090C3

+:10C110001B780D1C03930B78334211D005AC03221E

+:10C12000201C002105F040FA012306221E431A9B21

+:10C1300026701B786370A378934302221343A37085

+:10C140000DAF00211C22381C05F02EFA10222B788E

+:10C1500000263B701340591E8B4105AA5B421A40D2

+:10C1600001237A6104AA9373394B06AD9381311C84

+:10C170000322281C7E60FE603E61BE6105F014FA59

+:10C18000062201211A9B08AC5B78201C2B701A9B9D

+:10C190009B786B70AB789343039A0A4002210B4360

+:10C1A0000821D2008B431343102293430122311CF8

+:10C1B00013431422AB7005F0F7F907AB08936B46F5

+:10C1C0009B88391CA381009B221C18881C9B2681FC

+:10C1D000668126616560FFF7FFFE061E35D10123EB

+:10C1E00004AA93731B4B311C9381281C032205F076

+:10C1F000DBF906221A9B311C5B78201C2B701A9BE2

+:10C200009B786B70AB7893430222134308229343CD

+:10C2100010229343012293431422AB7005F0C4F91A

+:10C2200007AB029904A80893656000F031FD04ABE8

+:10C2300023611C9B80B21A1C2081A08108321888BF

+:10C24000211C6681FFF7CAFE061C301C15B0F0BD2C

+:10C250004D2A000008290000F0B595B005AF161C66

+:10C2600001900291381C00210322039305F09CF9F0

+:10C2700001221023134306223B7033780DAC7B70F0

+:10C28000BB78201C93430222002113431C22BB7065

+:10C2900005F08AF90F22B17823780A40511E8A41AD

+:10C2A00002210B430821D2008B4313431022134376

+:10C2B0002370012304AA9373131C6A460025128974

+:10C2C000676106AF9A81291C381C03226560E5600E

+:10C2D0002561A56105F068F906227378291C3B7079

+:10C2E000B37808AE7B70BB78301C934302221343B3

+:10C2F0000822934310229343012293431422BB70DC

+:10C3000005F052F907AB08930823B381019B211C68

+:10C310001888321C039B7760358175813561FFF782

+:10C320005BFE15B0F0BD0000F0B50B8887B0112B97

+:10C3300031D0041C112B02D8102B06D02FE1502B2A

+:10C340002FD0512B00D1A6E029E1BB228B885200CF

+:10C3500083524389002B00D121E16B46012701ADB7

+:10C3600000210822281CDE1CDF7005F01DF90023C7

+:10C370006B80BB232F806E605B00E05A61892A1CB2

+:10C38000FFF72EFE002800D109E16368002B00D1E1

+:10C3900005E1984703E10122BB2352425B00C252F0

+:10C3A000FDE0B7204000CB88205A8A1D984213D167

+:10C3B000138B012B00D0F2E02568002D00D1EEE0B8

+:10C3C000937E002B02D0012B00D0E8E001AA13706D

+:10C3D000201C01A9A847E2E06089984216D1138B7E

+:10C3E000012B00D0DBE02368002B00D1D7E0927E48

+:10C3F000002A04D0012A00D0D1E0062100E0052166

+:10C4000001AA1170201C01A99847C8E0A92040008A

+:10C41000205A984202D101A9624B12E0A7204000A5

+:10C42000205A984206D15F4B01AA138000239370D3

+:10C43000D3701DE0B1204000205A984209D15A4BD8

+:10C4400001A90B8000238B70CB70201CFFF73EFEF0

+:10C45000A5E0AF204000255A00209D4203D0251CB6

+:10C46000A67C183508E0504B01AA13809070D0705C

+:10C47000201C83E00130C0B2B0423FD00A352F1CEF

+:10C480000A3F3F889F42F5D1484B01A90B80012309

+:10C49000C8708B70D9E78A79012A00D07FE0251C0B

+:10C4A00000220889A67C1435D3B2B34206D22F8865

+:10C4B00001320A35B842F7D1012262E0231C251C63

+:10C4C000002278331E787A35D3B2B34206D22F8851

+:10C4D00001320A35B842F7D1022252E0231C251C52

+:10C4E0000022DE331E78E035D3B2B34257D22F8814

+:10C4F00001320A35B842F7D1032242E0221C002063

+:10C500001432B0420DD00A32151C0A3D2D889D42CE

+:10C5100004D1264A01AB1A8001222BE00130C0B2BF

+:10C52000EFE7221C0020783215780232A8420DD0A5

+:10C530000A32161C0A3E36889E4204D11B4A01ABC1

+:10C540001A80022216E00130C0B2EFE7221CDE3270

+:10C5500015780020023201E00130C0B2A8421ED09E

+:10C560000A32161C0A3E36889E42F5D10F4A01ABAC

+:10C570001A800322D870201C9A7001AAFFF736FD9A

+:10C580000DE02668002E0AD0072001AD287007486C

+:10C59000AA71A880EB71201C0591291CB04707B037

+:10C5A000F0BDC046222A0000332A00004D2A0000B8

+:10C5B0000722F0B58B7A9DB004920A2B00D9AEE227

+:10C5C0000B7C0A2B00D9AAE20B7E0A2B00D9A6E22B

+:10C5D0000A68059002604A680D1C42608B7A837479

+:10C5E000031C0A7C78331A700A7E66331A700122A3

+:10C5F000BB2352425B00C2520120CE4B0AA90B80E2

+:10C60000059B887008331A1C0693FFF7E1FC049021

+:10C61000061E00D083E22B1C36331B7815ACB342C8

+:10C620000DD9059BAA6B18897300995A221CFFF734

+:10C63000D1FC002800D04DE10136F6B2EBE7002333

+:10C6400004932B7A002B06D16B7A002B03D1AB7AA3

+:10C65000002B4AD183E000211C220226201C04F07A

+:10C66000A3FF0422237822213343134323700123A1

+:10C67000002704AA52181370AF4B04AA1384391C64

+:10C6800003220BA86760E76027616761A76104F078

+:10C690008BFF2B1C3C331A7804A90A775B788A7FBE

+:10C6A0004B7706239A43E72316431E4001239E43FC

+:10C6B0008E7710AE391C1422301C04F075FF012255

+:10C6C00009AB1A700CAA10920BAA72600122336196

+:10C6D000059B3281B28177811889211C321C0A3373

+:10C6E000FFF77AFCB842B2D0F4E00026EB68B34220

+:10C6F00035D0AB7AB34232D915AFEB6800210122B5

+:10C70000F400381C1C1904F04FFF02223B780598F6

+:10C7100013433B700F23A2791A40511E8A4108210E

+:10C720003B78D2008B431343102213433B70211DEF

+:10C7300022880091E179A31CC907C90FC9B20191F0

+:10C740000A2171434118143102910698391CFFF7F0

+:10C75000D7FC002800D0BDE00136F6B2C9E72B7C3B

+:10C76000002B04D000266B69B34200D0D8E02B7EAA

+:10C77000002B00D0D8E0022615AC00211C22201C82

+:10C7800004F012FF2378002733432370012304AA07

+:10C7900093776A4B391C938309A803226760E7608B

+:10C7A00027616761A76104F0FFFE2B1C2C331B7807

+:10C7B00004AA13752B1C2D331B78391C53750623C3

+:10C7C000927D9A43E72316431E4001231E4304AB88

+:10C7D0009E7510AE1422301C04F0E6FE0BAB1093D5

+:10C7E00009AB7360AB8C77813381B3812B6A211CD9

+:10C7F0003361059B321C18894533FF33FFF7ECFB8F

+:10C80000B84200D011E1EB1DDB7F002B00D1C1E06D

+:10C81000AB6A061C002B00D109E1EB1DDB7F9E42B9

+:10C8200000DAB9E02B7A002B00D007E16B7A002BFD

+:10C8300000D064E10598291CFFF7F2FB0490002862

+:10C8400000D06CE115AC04991C22201C04F0ACFE55

+:10C85000042223780127134322222370049B0BAE6A

+:10C860006360E36023616361A36104AB9B181F7085

+:10C87000334B04AA138404990322301C04F094FE61

+:10C8800006223E352B78022133706B7810AD737021

+:10C89000B378281C9343E7220B4313406A46BB43FB

+:10C8A000127CB37004AB1A750499142204F07CFE58

+:10C8B0000CAB10936B461B8A6E606B8109AB2B61CE

+:10C8C000059B2F81AF8118896F33FF33211C2A1CF0

+:10C8D000FFF782FB049022E115AF6B690021012272

+:10C8E000F400381C1C1904F05FFE0E233A78211D59

+:10C8F00013433B7022880091E1790598C907C90F5D

+:10C90000C9B201910A21714341187A310291A31CE5

+:10C910000698391CFFF7F4FB0028DBD10136F6B28C

+:10C920002B7CB342D8D822E70026EB69B3422DD145

+:10C9300021E7C046121800004E2A00004B2A0000D2

+:10C940004C2A000015AFEB6900210122F400381CCD

+:10C950001C1904F029FE0A233A78211D13433B7069

+:10C9600022880091E1790598C907C90FC9B20191E0

+:10C970000A217143E03141180291A31C0698391C29

+:10C98000FFF7BEFB0028A5D10136F6B22B7EB342DD

+:10C99000D8D8F0E61E1C40E7012404AB9C77614B1D

+:10C9A00009AF04AA938300210322381C04F0FCFD84

+:10C9B000202323433B70002306227B70BB78022197

+:10C9C0009343E7220B431340A343BB70AB6AB00011

+:10C9D0001818132304AA9B18191C0CAAFFF704FBB0

+:10C9E0000790031E1ED110AC191C1422201C04F049

+:10C9F000DBFD0BAB109304ABDB7C6A462381A38188

+:10CA00000CAB2361A223928B67606281059A5B0065

+:10CA1000D05A4D32211CFF32FFF7E0FA002804D132

+:10CA20000136FAE6079B049303E0049001E0072334

+:10CA300074E0049B002B00D1F4E670E02A1C059BF7

+:10CA400006984F33FF3338494332FFF705FC079010

+:10CA5000031E62D115AC191C1C220226201C04F0F6

+:10CA6000A3FD0822237822213343134304221343D6

+:10CA70002370049B04AA6360E36023616361A36184

+:10CA8000012352181370294B0BAF04AA1384079982

+:10CA90000322381C04F088FD2B1C46331B78062229

+:10CAA0003B702B1C47331B7807997B70BB789343F3

+:10CAB0001E4308239E4310239E4301239E43BE70C2

+:10CAC00010AE1422301C04F06FFD0CAB10936B46BB

+:10CAD0001B8A776033816B461B8A211C738101237B

+:10CAE000B381049B321C3361059B18895733FF3394

+:10CAF000FFF772FA0490002800D197E60FE02A1C95

+:10CB0000059B06985F330A494032FF33FFF7A4FBC9

+:10CB10000490002800D18DE601E0079B049304985F

+:10CB20001DB0F0BD07290000222A0000322A0000B3

+:10CB3000332A0000F0B589B000920193837C051C74

+:10CB40000F1C07208B422AD9BB235B00EA5A154BE6

+:10CB500008209A4223D002AB9E1D6B4604AC1B8872

+:10CB60000021201C1022338004F01EFD0A235F43A5

+:10CB7000EF19BB8A211C23800123A3700023A3800B

+:10CB8000019BA660E360BB235B00E85AFFF72AFA2B

+:10CB9000002804D13388009A934200D00C2009B0B9

+:10CBA000F0BDC046FFFF0000BB23F0B589B05B00BD

+:10CBB0000192C25A134B061C0F1C08209A421FD028

+:10CBC00004AC02ABD980201C002110229D1D04F072

+:10CBD000EBFCA7235B00F35A211C23800123A370E5

+:10CBE0000023A380019BA560E360BB235B00F05A98

+:10CBF000FFF7F8F9002803D12B88BB4200D00C20A6

+:10CC000009B0F0BDFFFF0000F0B5071CBB204000DD

+:10CC10008BB03E5A1D4D141C10AA12880820AE423B

+:10CC200032D0D01C01906846858802A8861D3580C8

+:10CC30000C20082D28D804AD29706C70AB70002A28

+:10CC400005D00B2002ABC018119904F06BFC06ACA8

+:10CC50000021201C102204F0A7FCAF235B00FB5A2C

+:10CC6000A66023800123A3700023A380BB23E5607B

+:10CC70005B00F85A211CFFF7B5F9002804D133886E

+:10CC8000019A934200D00C200BB0F0BDFFFF0000D2

+:10CC90000B7803704B7843700220704738B5041C42

+:10CCA000081C0D1C04F0CCFC6560208038BD7FDFC3

+:10CCB000704782DF7047A7DF7047A9DF7047AADFA0

+:10CCC0007047002070470020704700207047002008

+:10CCD0007047002070470020704700207047000018

+:10CCE000F0B585B0041C0F1C02AA81782248FDF71C

+:10CCF0001FFA00283ED10C236678204A5E4395191E

+:10CD0000AB7A01925B0605D4022F03D0012F03D02A

+:10CD10001B4D02E01B4D00E01B4D1C26A1781B4B58

+:10CD2000714302A85918321C0023A847002813D1C8

+:10CD3000022F1FD00C234021627802A85343019A8E

+:10CD4000D3189A7A8A439A726178114B7143321CD4

+:10CD50005918331CA84701E0022F0BD00C2322786E

+:10CD600002A853430B4A211CD3189B7A0A4A9B0002

+:10CD70009B58984705B0F0BD042B00204E2A002098

+:10CD8000C3CC0100F1A101007DA101005C2A0020BB

+:10CD9000B02A0020F42A0020D01B0200F7B5282278

+:10CDA0000190437829485A4381184C68284F002C39

+:10CDB0000FD00C2159437918897A4DB2002D07DA2A

+:10CDC000490605D4151C0835A6B24519145802E0C9

+:10CDD0000024261C251C0C225343FB181889291CEF

+:10CDE000321C231CFFF769FF0B2818D10C22019B72

+:10CDF000291C5B785343FB181889321C0123FFF769

+:10CE00005CFF00280ED00C21019B124A5B784B433B

+:10CE1000FF183889FFF74FFF00280AD115E00028D6

+:10CE200003D100E00124002000E00B48012C0CD1CC

+:10CE3000E9E70222094BC318934303D0082801D025

+:10CE4000112801D1044800E00020FEBDCC2A0020BA

+:10CE50004E2A0020FFFF000041800000FECFFFFFB0

+:10CE600008B5FFF79BFF08BD2023F0B58DB002AADF

+:10CE7000D3800C2201904B780E1C5343254A04A901

+:10CE8000D318188902AB9A1D0323FFF718FF002857

+:10CE90003CD128247078204F604302AB0830DA88F8

+:10CEA000381804A904F014FB3D1C737800280DD138

+:10CEB0000C225A4317498A18917A0222114025D12F

+:10CEC0006343221CF81804F06FFB1FE05C433C191D

+:10CED0006268002A01D1114F00E0114F282403227B

+:10CEE0006343EA5002ABDA88737804A96343EB1812

+:10CEF0005A60707860430830281804F013FB71788A

+:10CF000001986143221C69183823B84700200DB0EE

+:10CF1000F0BDC0464E2A0020CC2A00207DA1010091

+:10CF2000F1A1010008B5FFF79FFF08BD70B52824E7

+:10CF3000031C4878094D60430E1C2818191C221C3C

+:10CF40003823FDF78FF9002806D173785C432C193C

+:10CF50006368013300D1606070BDC046CC2A0020F8

+:10CF600008B5FFF7E3FF08BDF0B5504C95B0256854

+:10CF70000294002D00D197E01A9C782C00D993E000

+:10CF80000024161CFF221D1C05AB5A709A70DA7023

+:10CF90001C701A9B08AF019103903C700D950C9387

+:10CFA000A1B2434806AAFDF7C3F8002809D10398A7

+:10CFB00006A9082204F08CFA002802D105AB9C7067

+:10CFC00002E00134032CEBD105ACA378FF2B6BD02E

+:10CFD000019908AA042907D11A9B782B01D1202393

+:10CFE00000E04023137045E03249691A532910D8F4

+:10CFF0003149002E04D1012098400D68054253D0DC

+:10D0000001209840031C086898432023086013708F

+:10D010003EE02A49691A1B2908D8202313700023EF

+:10D0200063700EAB0D9318230C9331E02448291A3A

+:10D0300027290ED830233B70002363700123E3704F

+:10D04000204B997A0223194022D1282204F0ACFA0D

+:10D050001EE0402111701C4A95420BD01B4A9900DA

+:10D060008A58AA4220D1174AD27A9A4210D1002374

+:10D0700063700DE0032108AA13780B4313700BAB08

+:10D080000993029B201C1B6808A9321C98470BE0DF

+:10D09000019A08AB032A02D11A78012101E002218A

+:10D0A0001A780A431A70EAE715B0F0BDF42A002096

+:10D0B000042B00205C2A0020002B0020B02A002036

+:10D0C000CC2A00204E2A0020001C02003C2A00200E

+:10D0D00070B51C2444430F4E051C3019002134192F

+:10D0E0000622113004F060FA201C0021102204F006

+:10D0F0005BFAFF232374A37601235B42238301237E

+:10D10000AB40054AAD001168994311600022034B02

+:10D11000EA5070BD5C2A0020002B00203C2A002031

+:10D1200037B581B2051C6A460748FDF701F8041EB1

+:10D1300008D168467821FDF7E5F8041E02D1281CC5

+:10D14000FFF7C6FF201C3EBD042B0020F0B585B0C4

+:10D1500000285CD00127002601252E4B041C2E48F8

+:10D160007F421E605E729E72311C0781C772072269

+:10D17000857204F019FA311C2822284804F014FAA8

+:10D18000301CFFF7A5FFFF23254E281C3370FFF747

+:10D190009FFF02207770FFF79BFF78236A469380FA

+:10D1A0000323B7701F4FD3801F4B6846391C009371

+:10D1B000FCF734FF00282BD11C4B24781D701D1C5C

+:10D1C000002C1AD11A4FA1B2164802AAFCF7B0FFE0

+:10D1D000061E0BD11C22101C604302A93818331CF8

+:10D1E000FDF740F8002804D02E7011E000232B70CA

+:10D1F0000EE00134032CE6D10AE06B466A46DB8878

+:10D200009188381C594389B2FDF77CF800E0094841

+:10D2100005B0F0BDF42A00204E2A0020CC2A0020C0

+:10D22000482A0020042B002069CF01004C2A00204E

+:10D230005C2A00200E800000F8B5174B1B78002BED

+:10D2400024D0002824D0002922D00B68002B1FD026

+:10D25000124D2C68002C1DD1061C281C0F1C08C068

+:10D260000531052204F05EF90120022104223B79F8

+:10D27000AB722B7A83430B4393432B72EB790343BB

+:10D280000B439343EB71201C347004E0044802E02C

+:10D29000044800E00448F8BD4C2A0020F42A00208D

+:10D2A000088000000E80000004800000F0B5254BCF

+:10D2B0001B78002B25D0002821D000291FD00B6817

+:10D2C000002B1CD08B68002B19D00378002B18D1B1

+:10D2D0001D4A1268002A14D01A1C1C1C1B481C4D25

+:10D2E000EF7ABB420FD1AF7A02252F420BD0013328

+:10D2F0001C30032BF3D10C710A73002020E0154879

+:10D300001EE015481CE00F7BBA4209D20125877E3A

+:10D310002F4205D18D6897007851104DAB540132E2

+:10D320000F79BC42E3D20225877E2F42DFD1102540

+:10D33000AC46844466460D68A7007E510134D6E7AA

+:10D34000F0BDC0464C2A0020F42A00205C2A0020B0

+:10D350004E2A00200E80000008800000482A00208D

+:10D3600008B50A4B1B78002B0ED000280AD0037892

+:10D37000002B09D1064B1B68002B05D08078FFF7E6

+:10D38000CFFE02E0034800E0034808BD4C2A00201D

+:10D39000F42A00200E8000000880000038B5104BF1

+:10D3A0001B78002B19D0002815D00478002C14D13C

+:10D3B0000C4B1B68002B10D0201C0B4D1C2363430F

+:10D3C000EB189B7EFF2B02D0201CFFF7A9FE013437

+:10D3D000032CF3D102E0054800E0054838BDC04603

+:10D3E0004C2A0020F42A00205C2A00200E80000035

+:10D3F0000880000037B5214B1B78002B3AD000285D

+:10D4000036D0002934D00378002B33D11C4B1B6855

+:10D41000002B2FD04378002B2ED11A4B9B7A012B57

+:10D420002AD08278022A27D81C235343164AD318BD

+:10D430009B7EFF2B20D00C1CC968051C00290AD03C

+:10D44000A268531E1E2B06D82378012B03D10F4848

+:10D45000083004F067F8A9786A460D48FCF768FEC2

+:10D4600023780C4A9B009B586846291C984704E087

+:10D47000094802E0094800E009483EBD4C2A002066

+:10D48000F42A00204E2A00205C2A0020CC2A00200A

+:10D49000042B0020D01B02000E800000088000003A

+:10D4A000108000007FB5294B051C1B78002B48D04D

+:10D4B000002844D0002942D08B68002B3FD003784D

+:10D4C000002B3ED1224B1B68002B3AD08378022BD5

+:10D4D00039D81C225A431F488218927EFF2A32D024

+:10D4E0004A680720032A2FD90C1C1B4899B202AAAC

+:10D4F000FCF71EFE002827D101A802A904226423FC

+:10D50000FCF7B0FE002804D1019B013301D0134E7B

+:10D5100000E0134E211D02A804226423B047002816

+:10D5200012D102A8A16810226823FCF761FE00282E

+:10D530000AD1AB78A1680B4A9B00995004E00A48D5

+:10D5400002E00A4800E00A4804B070BD4C2A0020FE

+:10D55000F42A00205C2A0020042B0020F1A1010005

+:10D560007DA101003C2A00200E8000000880000000

+:10D57000108000001FB5244B1B78002B3DD00028E5

+:10D5800039D0002937D00378002B36D11F4B1B68C8

+:10D59000002B32D08378022B31D81C225A431C4CEA

+:10D5A000A218927EFF2A2AD08C681A4A9B00002C6F

+:10D5B00002D1D3588B6000E0D4500C1C02AA8178B1

+:10D5C0001548FCF7B5FD00281CD101A802A90422CA

+:10D5D0006423FCF747FE002813D1019B013310D0D0

+:10D5E000A06802A910226823FCF73CFE002809D19C

+:10D5F000019B636006E0094804E0094802E009482D

+:10D6000000E0094804B010BD4C2A0020F42A002094

+:10D610005C2A00203C2A0020042B00200E80000001

+:10D620000880000010800000408000001FB51E4BE5

+:10D63000041C1B78002B31D000282DD00378002B40

+:10D640002CD11A4B1B68002B28D08178022927D8AF

+:10D650001C204843164B1818837EFF2B20D089B21C

+:10D66000144802AAFCF764FD00281AD1642301A81B

+:10D6700002A90422FCF7F6FD019B013311D002A898

+:10D680000D4904226423FCF7B3FD002809D1A378D7

+:10D690000A4A9B00985004E0094802E0094800E06B

+:10D6A000094804B010BDC0464C2A0020F42A0020CE

+:10D6B0005C2A0020042B0020001C02003C2A0020D1

+:10D6C0000E8000000880000010800000002806D0B6

+:10D6D000FF23037043708370C370002000E0014893

+:10D6E0007047C0460E80000010B5194A031C12781E

+:10D6F000002A29D0002825D0002923D00478002C26

+:10D7000022D1144A1268002A1ED09A78081CFF2AD7

+:10D710000AD15978FF2919D00C2359430E4B5918B7

+:10D720008B7A9B0712D508E01C21022451430B4B36

+:10D7300059188B7E1C4009D11031072203F0F2FEEC

+:10D74000201C04E0064802E0064800E0064810BD40

+:10D750004C2A0020F42A00204E2A00205C2A0020B7

+:10D760000E800000088000000580000013B5041C36

+:10D7700081786A460B48FCF7DBFC002810D11C239B

+:10D780006078094A5843694610181A1CFCF76AFD6C

+:10D79000064B074A9B7A68469B009B58211C98477A

+:10D7A00013BDC046042B0020B02A0020F42A00201C

+:10D7B000E01B0200F0B5FF24C34B8FB01B7803942D

+:10D7C000002B00D1D4E1C14B1A680093002A00D18C

+:10D7D000CEE105AF031D7B602A2300223B81038835

+:10D7E00004AD6C70AC70EC70061C2A70B84C102B39

+:10D7F0000AD0A27A8188900766D5B648814208D0BF

+:10D800002089884260D104E00121A37A0B4205D12E

+:10D81000AEE1E17AA9700021697055E00223A3729C

+:10D8200011236A703B708388871D23810722201C87

+:10D83000391C03F077FE0123327D134006D0A64B3E

+:10D8400052089B5CFF2B32D0039330E01C1CA34E8C

+:10D850001C20604310303018391C072203F038FEBA

+:10D86000002801D1039422E00134032CF0D102E01E

+:10D87000F379034240D10024009B05AE1B68281CAD

+:10D88000311C221C98473378122B00D070E190484D

+:10D89000837A012B00D16BE10123837201235B4268

+:10D8A0000381C3720021072203F07EFE60E108209D

+:10D8B000874A039B917ADBB20143D3729172281C91

+:10D8C000AB70FFF753FFD6E7172B5FD1012704207A

+:10D8D0001424009B00995B7A8243BB434B727C4BC0

+:10D8E00005A90C709A72B479191C002CC0D0D87A92

+:10D8F000FFF716FCB479BFE7AC78FF2CBBD0082348

+:10D900001A43F37A8A729B0706D51C236343734A32

+:10D91000D3189A7EBA439A76CB7AFF2BABD01C26CB

+:10D9200066436E4F0B78BE19022B09D0301C0722BC

+:10D93000103003F0F7FD0222B37E9343B37606E086

+:10D940000121674B5B8A3383B37E8B43B37600261A

+:10D950000197A6420FD0019B9B7EDB070BD41C20B6

+:10D96000604301993818102203F0B2FD002802D15B

+:10D97000301CFFF7D5FB019B01361C330193032EAE

+:10D98000E7D1281C0121FFF7ABF974E7172B00D969

+:10D99000B7E0132B5BD113233B7004231A43E37AC4

+:10D9A000A272FF2B2CD14D4B9B7EFF2B0BD04B4BF0

+:10D9B00036331B78FF2B08D0484B022752331B7895

+:10D9C000FF2B2BD102E0002700E001272378FE2265

+:10D9D000022B09D01C207843404B07221818103026

+:10D9E000211C03F09FFDFD221C237B433B49CB18E8

+:10D9F0009A7603AB1F70039BDBB2AB70E3720BE054

+:10DA0000162338482022083000213B7003F0CCFD5B

+:10DA10004022A37A1343A372002400E0324C002179

+:10DA20002B4B0B91DA7A1C2353432C4A0D91D318BC

+:10DA30000C932B4B09910893009B0A911A1DB088F7

+:10DA400008ABFFF734F9002800D115E790E0142B5C

+:10DA500029D1E37A214FFF2B1AD1F389398B9942CF

+:10DA600009D0B98E994208D0391C503109889942A1

+:10DA70000ED1022302E0002300E0012308210393DA

+:10DA80000A43DBB2E372A272281CAB70FFF76EFE92

+:10DA9000E37A0021FF2B00D01149B0883A1C002303

+:10DAA000FFF707F964E0112B62D10223111C99439F

+:10DAB000A17212071AD54BB2002B1EDA281C0021C6

+:10DAC000FFF70EF919E0C0464C2A0020F42A002086

+:10DAD0004E2A0020FFFF0000482A00205C2A002078

+:10DAE000B02A0020CC2A002043800000A878FF281C

+:10DAF00003D0FFF7EDFAFF23AB701023A3721223BC

+:10DB000002E01C2B02D113233B70B4E6522B07D149

+:10DB1000009B184A9B7A281C9B009B58984727E03B

+:10DB2000182B25D1B379112B14D1130712D5124814

+:10DB300020220830002103F037FD162360223B70BD

+:10DB4000A37A13430222A372009B5B7A1343009AC9

+:10DB5000537290E680235B421A4315233B70009B6F

+:10DB6000A2729B7A034A9B009B58281C984782E626

+:10DB70000FB0F0BDF01B0200CC2A0020A12303212E

+:10DB80000B4ADB00D150802300210A4A9B00D15070

+:10DB9000094B5961012111615969064A0029FBD0DD

+:10DBA000002305494B6153601360044A138070479A

+:10DBB00000F0014000100040FC100040162B002037

+:10DBC000A8220021124BD20030B59950A722114C47

+:10DBD000D2009C50104C114A03259C50A124104A9D

+:10DBE000E4001268520002321A510E4A0E4C9C5048

+:10DBF00080240E4AA40051601D51884203D0116157

+:10DC000001225A6003E00A4A11680A4A995030BD5D

+:10DC1000001000405B060000555555003C05000013

+:10DC2000182B0020040500001D2B0020FC100040D4

+:10DC30001C2000200C050000002238B5434B0121B8

+:10DC40005A608022D20511605A68002AFCD0002256

+:10DC500001213F4B3F481B6880245960D9601A500E

+:10DC6000181CFC3042648264C2640265C1206402F4

+:10DC700080001C50802080001950A2200424C00085

+:10DC80001C50A820344CC0001C508224334864002F

+:10DC90001C501960324B1A60324B1B681B0716D49C

+:10DCA000314B324A1968324B9950324A1168E52299

+:10DCB000D2009950304A1168304A9950304A116860

+:10DCC000E622D20099502F4A11682F4A99502F4BC3

+:10DCD00004201D68FC232C1C9C4335D12B1C2833AD

+:10DCE000DBB22C2B30D8FFF749FF214B284A032108

+:10DCF0009D50A222FF20D2009950264A9958814374

+:10DD0000712099509958014399502349234A04207E

+:10DD10009950A6210122C9005A502149FF305C5078

+:10DD2000204958508420204940005850A3211F48C2

+:10DD3000C90058501021201C1D4B1C701D4B1A701F

+:10DD40001D4A13690B43136138BDC046FC000040F7

+:10DD500014200020040500007102000044050000AA

+:10DD6000442B0020AC000010EC0000102407000041

+:10DD700000100040F0000010F40000102C0700001C

+:10DD8000F8000010FC000010340700001C200020E8

+:10DD90000C05000024050000002941761C05000048

+:10DDA0002C050000340500001405000025000300C8

+:10DDB0001C2B0020142B002000ED00E0102138B5B2

+:10DDC0002F4A304D1369304C0B4313612F4A304BAF

+:10DDD0001169002936D000211161C02202202D498D

+:10DDE000520088502A78042A2CD101225A608022BD

+:10DDF000D2009B58012B20D10F2321786278194043

+:10DE000081421AD8252A18D800290AD12018224977

+:10DE100003F05EFB002810D1204A138801331380E1

+:10DE20000BE0012900D055230021C8B29042F3D263

+:10DE30000131601840789842F7D0201C0021272239

+:10DE400003F0B2FB164B1B68FC335A6C002A02D05D

+:10DE500000225A64BAE7996C0029B7D09A64114B32

+:10DE6000012219781F230B409A40131CC022094934

+:10DE700052008B500C4B1A68501C186038BDC046BD

+:10DE800000ED00E0142B00201D2B0020FC100040B2

+:10DE90000010004000E100E0041C0200162B0020EE

+:10DEA0001420002018200020442B00200021044BC7

+:10DEB0001A781970034B1B880360101C7047C0460A

+:10DEC0001C2B00200C2B0020084A031C1278002079

+:10DED000012A09D81A1C2832D2B22C2A04D89A074F

+:10DEE00002D10120024A13607047C046142B002063

+:10DEF0001C2000200E4B1A780023012A16D89842C5

+:10DF000004D10C4A0C4B1A6008220CE0012804D101

+:10DF10000A4A094B1A60092205E0022806D1084A7C

+:10DF2000054B1A600A22074B1A700123181C704710

+:10DF3000142B00200080004014200020009000409E

+:10DF400000A0004018200020F7B56D46041CD0B298

+:10DF500001902D7960480127057000255F48036016

+:10DF60005F4801605F4807705F4805805F48007840

+:10DF7000844666460520AE4200D1ABE0AC420DD1EE

+:10DF8000A8235B4ADB00D450A1225A4BD2009F50F9

+:10DF9000A222D2009C50584A9C501BE0032C1ED158

+:10DFA000012E04D10123504A0220138092E0534BEA

+:10DFB000534A1B8813434C4A1380A8234C4ADB0066

+:10DFC000D550A1224B4BD2009F50A222D2009D508F

+:10DFD000494A9D50FFF7D2FD444B1F7079E06046DF

+:10DFE000012EDFD1272902D93F4B1E8072E0434822

+:10DFF0000580012C09D1291C2722424803F0D4FABC

+:10E00000201CFFF7DDFD042361E0281C022C61D1F8

+:10E010000198252803D9344B03201E805AE00F2491

+:10E02000181C384D20402870684600796870281CFC

+:10E03000012B0BD0002B04D0022B0AD001330DD0C2

+:10E040002EE03149023003F06DFA2EE0A81C211CAD

+:10E0500001E05521023003F0A7FA26E00020032A50

+:10E0600038D8101C03F020FA020212180020FFF723

+:10E07000A7FDA82103221E4BC90080205A50012170

+:10E08000800019501960194B1A7022E0081CFFF724

+:10E090001BFF002807D01CE0081CFFF72BFFF8E748

+:10E0A0000123114A1380042014E00020FFF788FDAB

+:10E0B000A221164BC9001A680E4B4132FF325A504A

+:10E0C0000B490D4A99500121114A99500223074AE0

+:10E0D00013700020FEBDC046152B0020102B002021

+:10E0E000182B00201C2B00200C2B0020142B0020B0

+:10E0F0000010004000F0014014050000162B002025

+:10E100000080FFFF1D2B0020041C020014200020B3

+:10E110000405000010B51A490B78012B19D1194BD1

+:10E120001B681B0715D1184BE0241A68F023101C3C

+:10E13000184022420BD1154A12681A4209D1144ADA

+:10E14000144B1A608022144B12021A6001E030282E

+:10E15000F1D00978012912D10A4B1B681B070ED197

+:10E16000F022094B1B681340402B08D1074B1B685A

+:10E17000134204D1C1238022DB00D205D15010BD4F

+:10E18000E00F00F0E40F00F0E80F00F0EC0F00F0FB

+:10E19000DFFF07C004050040186C00402EDF704709

+:10E1A00051DF704710DF704711DF704761DF704744

+:10E1B00008B5081C111CFCF705FC08BDF0B5274B81

+:10E1C00085B01B78002B47D0254D264E2B68346830

+:10E1D0005A425341DBB2009363425C41E4B2009B7C

+:10E1E000002B37D103A8FFF7DBFF05280AD0002852

+:10E1F00004D000210A1CFCF7C1F905E003982B6844

+:10E20000984701E001230093002C1FD1164B03A96E

+:10E210001B8801240B80154B1B68181C0193FFF70A

+:10E22000C5FF071E052F0BD0002805D000210A1CB2

+:10E23000FCF7A4F9002403E00198336898473C1CDC

+:10E24000009B002BCED0002CC9D005E0009B002BFA

+:10E2500002D1C7E7002CD9D005B0F0BD482B002073

+:10E26000502B0020542B00204A2B00204C2B002048

+:10E2700038B5041C0720002913D08D0711D1094897

+:10E2800001600949201C0A80084A09491360FFF708

+:10E2900089FF002805D10122064B16201A70FFF7CE

+:10E2A0007DFF38BD4C2B00204A2B0020582B00202E

+:10E2B000B1E10100482B002008B5FFF775FF031CF2

+:10E2C0005A1E9341014ADBB2137008BD482B00204F

+:10E2D0000E23002802D0024B18600023181C704740

+:10E2E000542B00200E23002802D0024B186000237C

+:10E2F000181C7047502B002008B5074B1B68002BDB

+:10E3000007D09847002806D000210A1CFCF736F9F0

+:10E3100001E0FFF753FF08BD582B002008B5002986

+:10E3200004D00020011C021CFCF728F9FFF746FF6F

+:10E3300008BD0000002008B5011C024AFAF7EAFEF9

+:10E3400008BDC0461DE30100A023EF220021DB052C

+:10E35000D20070B5012099501D1C20220426094CC2

+:10E36000011C9140214204D0111CC131FF318900B0

+:10E370004E51013AF4D2A321034AC9005A5003492D

+:10E380005A5070BD5300C022AC9F05080C05000018

+:10E39000A023A321134ADB05C90070B55A500028F9

+:10E3A00004D0A121C9005A50022001E00E495A5060

+:10E3B0000422A0262023012400040C4D0243F6056C

+:10E3C000211C9940294204D0191CC131FF31890018

+:10E3D0008A51013BF4D2EF23A022DB00D205D050BA

+:10E3E00070BDC046AC9F05080C0500005300C0225C

+:10E3F000A223A0220120D205DB00D3589B0F984313

+:10E400007047000030B5A024202301210B480C4D9B

+:10E41000E4050A1C9A40024204D01A1CC132FF32A1

+:10E4200092001551013BF4D2A023A321054ADB053C

+:10E43000C9005A50A121C9005A5030BD5300C02212

+:10E4400004000200AC9F05080022F0B587B00A70F6

+:10E450000D230292A02203900193D205A325019BD4

+:10E460003A48ED005C1E019469D3394B394CD050C9

+:10E4700010515051D050019C374BE35C0124261CB5

+:10E480009E405651A125ED005651C046A223DB0007

+:10E49000D5582E02F60FA640AF0927403E432F1C49

+:10E4A000AB07DB0F9B001E43EB06DB0FDB001E43BD

+:10E4B0002740AB003F01DB0F3E435B0133436E025D

+:10E4C000F60FB6011E43AB01DB0FDB01019D3343A9

+:10E4D000224EAD00AB51002BC0D0019D0026EF00B5

+:10E4E0001F4D0594ED1904950825013D17D3059F8F

+:10E4F000AF401F42F9D00F78052F0ED8049C675DFE

+:10E50000BC466446039F3C703C1C013403940C7869

+:10E51000A4460124A44464460C700136E5E7012EAC

+:10E5200009D9029C1C4206D0A323A022DB00D205FD

+:10E53000D050002007E00298184302908EE7A023F5

+:10E54000DB055851012007B0F0BDC046AC9F05085F

+:10E550001C0500000C050000941C02005C2B002030

+:10E560002C1C0200F0B50023042A1AD1082502262B

+:10E570000127041D0178132901D13B430AE0042936

+:10E5800001D1334306E00C2901D1134302E01529E0

+:10E5900000D12B430130A042ECD10F3B5A425341F2

+:10E5A000DBB2181CF0BD2EDF704732DF704733DF5F

+:10E5B000704712DF7047000070B52D4C6368002B68

+:10E5C00003D1A368002B0FD10AE00025294E656016

+:10E5D000337A9D42F5DAAB009B5998470135F7E74E

+:10E5E000E368002B11D10CE00025214B214E9D60EA

+:10E5F000337D9D42F4DAAB00F318DB6898470135B0

+:10E60000F6E72369002B18D10DE00025184B194EB1

+:10E61000DD60731CDB7F9D42F3DAAB00F3189B696E

+:10E6200098470135F5E7BE23134A5B00D15800290E

+:10E630001DD000240F4DD4500EE000240C4B0D4D86

+:10E640001C612B1C2C331B789C42ECDAA300EB18CA

+:10E650005B6A98470134F4E72B1C38331B789C42E3

+:10E6600005DAA300EB181B6B98470134F4E770BD83

+:10E67000FC600040902B00200060004037B5002077

+:10E680006B46164CDD1DD8712378834224D1281C9B

+:10E69000FFF78FFF00281FD12B78012B0AD1032110

+:10E6A0000620FFF784FF0620FFF77FFF0620FFF715

+:10E6B0007AFF0EE0C1210A4A89005058094B0340F5

+:10E6C000C020000403435350C0214023490053504D

+:10E6D00013600123002023703EBDC046CC2B0020D8

+:10E6E00000E100E0FFFF00FF30B50723042838D821

+:10E6F0000C2242431C4D0423AA18147A012C30D852

+:10E70000032343431B199B00013414725951C12444

+:10E710004A1E91410138C9B2144A0023A4000328BB

+:10E7200004D8251C02F0C0FE060A101410484360ED

+:10E73000115116E00E484900836002E00C48890040

+:10E74000C360515100230CE00948C9000361F8E798

+:10E75000002901D0802109061151BE210023490062

+:10E760005350181C30BDC046902B00200060004064

+:10E77000FC6000408122F8B52648274B5200C25069

+:10E78000A0230124DB00C4508022A023A121002566

+:10E79000C900DB0592035A5020494D6004604C6863

+:10E7A0001C48002CFBD01E4901255A50A123DB0038

+:10E7B000C3581C4C45605C431B4802F045FD201CBF

+:10E7C00002F06AFD194802F03FFD194B64209C429B

+:10E7D0001ED82B1C174A990089588C4214D9164808

+:10E7E0005D1EC65C475D601AFA24AD00AB58A400FC

+:10E7F000591A604302F034FEBF1B7843211C02F01B

+:10E800002FFE3018C0B203E00133072BE3D1002004

+:10E81000F8BDC0460070004004050000FC700040D8

+:10E820000C050000D5100000A41C0200B71C02005B

+:10E830000B794100C81C0200C01C0200A0238022EA

+:10E84000A321DB059203C9005A50024900205A5007

+:10E850007047C0460C05000074DF704776DF7047D4

+:10E8600078DF70477ADF70477CDF70477EDF704764

+:10E8700080DF704760DF70472FDF7047002330B5BF

+:10E8800087B001A80193FBF76DFD041E1BD1019D0C

+:10E890000123002D07D00D4A137010230C4A116874

+:10E8A0000B4313600FE002A98860C86009480B7031

+:10E8B000FEF7A0FD0823021C041C9A4303D0291C68

+:10E8C0002A1CFBF75BFE201C07B030BD692E002020

+:10E8D000F02E0020B42E00201FB50368041C187809

+:10E8E000072850D802F0E0FD54540454283D3F431B

+:10E8F000284A13780133DBB21370274A12789A4200

+:10E9000046D8264B01A81B689A8DC2801A8D42807A

+:10E91000DA8C02805B8D8380FCF7D6FF112805D04E

+:10E92000002803D000210A1CFBF728FEFFF7A6FFF2

+:10E93000002100282CD028E0A268002A03D1101C56

+:10E94000111CFBF71BFE032301A80370237903713D

+:10E9500063794371A3798371E379C371A368836099

+:10E960000CE0012200E000220D4B1A7010E004239D

+:10E9700001A8037023790371A3794371094B0C211A

+:10E980001B68984704E00020011C0A1CFBF7F6FDF9

+:10E990001FBDC046F42E0020FC2E0020EC2E0020CF

+:10E9A000502E00201C2C0020F0B589B0061CFBF76F

+:10E9B0003FFA022E7AD1404B1A780193002A75D083

+:10E9C000002403A80394FBF7CDFCA04203D0211C34

+:10E9D000221CFBF7D3FD039D002D67D10122374C8C

+:10E9E0002368134218D093432360354B1868FBF714

+:10E9F00037F8002803D0291C2A1CFBF7BFFD00258F

+:10EA000004A80C212F4A0570FAF784FBA84203D012

+:10EA1000291C2A1CFBF7B2FD0422236813420ED0E6

+:10EA2000934323600D2304A80C21264A0370FAF7B0

+:10EA300071FB002803D000210A1CFBF79FFD08276B

+:10EA400023681F400AD008221F489343236000F028

+:10EA500069FE002825D000210A1C20E020221D1C70

+:10EA6000154009D093431848236000F0AFFE0028FA

+:10EA700017D0391C3A1C12E01022134211D09343D4

+:10EA80002360012304A910480B708D60CD60FEF750

+:10EA9000B1FC0822031C934303D0291C2A1CFBF75A

+:10EAA0006DFD2368002B01D1019A1370301C01F019

+:10EAB000ADFA09B0F0BDC046692E0020F02E00204E

+:10EAC000602E0020B1ED0100B42E002037B5041CEB

+:10EAD0000D1C0B78002A06D0322B04D1FFF7CEFE96

+:10EAE00000280DD008E0142B03D1842A08D0812AF5

+:10EAF00006D0002A04D0101C00210A1CFBF73EFDA2

+:10EB00002B78152B37D004D8112B23D0142B2CD0D5

+:10EB10005EE0212B29D0222B5AD16846FBF722FC3C

+:10EB2000002803D000210A1CFBF728FD009C002CC4

+:10EB300007D1284800F0F6FD002849D0211C221CEE

+:10EB400044E00122244B1A700823244A11680B4325

+:10EB500013603DE0A378FF2B3AD08021204B49027F

+:10EB600018680022FAF726FF32E0211C0422194817

+:10EB700002F0D8FC2CE001221A4B1A701A4B1C78B8

+:10EB8000002C15D16946194B68461B689A8DCA80BE

+:10EB90001A8D4A80DA8C0A805B8D8B80FCF794FE9C

+:10EBA000112805D0002803D0211C221CFBF7E6FC0D

+:10EBB0000122A0210E4B49021A700E4B0022186848

+:10EBC000FAF7F8FE002803D000210A1CFBF7D6FC58

+:10EBD00000203EBDB42E0020692E0020F02E002023

+:10EBE0005C2E0020E82E0020342C0020EC2E00208B

+:10EBF000202C0020602E002070B5027F041C07200E

+:10EC0000042A1DD81C230F49024095000A780226C9

+:10EC10009A432A4313400C3B5D1EAB410125AB4098

+:10EC2000B2431A4310232A430A7048708B70637FE3

+:10EC300000201D40D3B220226D0193431D430D706F

+:10EC400070BDC046642E00201FB50B78002B05D187

+:10EC50000C21034B01A801701B68984705B000BD4B

+:10EC60001C2C002008B5072805D0002803D000215F

+:10EC70000A1CFBF783FC08BD10B5084B1C78002C60

+:10EC80000AD1074B07491888FFF7F0FD002803D089

+:10EC9000211C221CFBF772FC10BDC046E82E002090

+:10ECA000322C0020642E002008B5002803D000215B

+:10ECB0000A1CFBF763FC08BD13B51D4B1A78002A2C

+:10ECC00029D01C4A1278002A25D01B4A11881B4AD9

+:10ECD000914220D0002401A81C70FBF743FBA04206

+:10ECE00003D0211C221CFBF749FC019C002C09D1FC

+:10ECF000134800F06BFD002819D0211C221CFBF7E3

+:10ED00003DFC14E001220F4B1A7020230E4A1168BB

+:10ED10000B4313600BE00B4B1B78002B07D0BFF3AA

+:10ED20004F8F0A4A0A4BDA60BFF34F8FFEE713BDDD

+:10ED3000202C0020E82E0020322C0020FFFF0000B5

+:10ED4000B42E0020692E0020F02E00200400FA05C9

+:10ED500000ED00E007B5124B122012494822FFF7E0

+:10ED600087FA002803D000210A1CFBF707FC0822C1

+:10ED70006846002102F018FC6B4601221B786846A9

+:10ED800013436A461370FFF775FD002807D1064844

+:10ED9000FFF79EFA002802D10448FFF7A3FA0EBD40

+:10EDA00035E301006C2E002079F40100A9E901008F

+:10EDB00038B5051C16200C1CFFF75EFD002803D09B

+:10EDC00000210A1CFBF7DAFBFFF776FA002803D0D4

+:10EDD00000210A1CFBF7D2FB0C2C04D1024B281C8F

+:10EDE0001B68211C984738BD1C2C0020F0B50B1C5B

+:10EDF0008DB00721002800D1D4E0041CC0206B4D49

+:10EE00004002191C38220193286002F061FB00289F

+:10EE10001CD08023664A5B051E695169013105D00B

+:10EE20005069196989B202F01BFB00E0586946433A

+:10EE3000604B9E4204D80020011C021CFBF79EFB85

+:10EE4000C020019940020E2202F0B6F95A4B5B48ED

+:10EE50001C6000242B68211C5A6B594B1A6001223C

+:10EE6000584B52421C70584B1C70584B1C70584BDE

+:10EE70001A80584B48221C7002F096FB564F211CFA

+:10EE80000522381C02F090FB211C1822534802F086

+:10EE90008BFB211C0222524802F086FB514B524E42

+:10EEA0001C70524B211C5248524A1C703460FAF7B5

+:10EEB0005BFD011EA14275D12B6806A8DA8D028286

+:10EEC0001A1C3032127882741A1C31321278C274D1

+:10EED0001B7F0375484B83608023DB00C360474B77

+:10EEE0000693474B436001F017F9011EA14259D127

+:10EEF000FFF730FF011EA14254D1FBF733F8011E8A

+:10EF0000A1424FD12868FFF777FE011EA14249D1E7

+:10EF1000019B3C480433DD7FFEF7D8FB011EA14274

+:10EF200033D102AB181C1D70FEF710F9011EA1426F

+:10EF30002BD1092002ABC018052202F035FB3B782B

+:10EF40000120191C03AD6A79014082435C0804402A

+:10EF50000A43022184408A4322431C211C24194075

+:10EF6000A2430A4320215B0903405B018A431A4301

+:10EF70006A717B78291CAB71BB782871EB71224BCD

+:10EF800022480393FEF758F9011C00290AD10123F6

+:10EF9000144A1F481370326813431E4A3360FAF74D

+:10EFA000E3FC011C081C0DB0F0BDC046EC2E002097

+:10EFB000001000100D8001001C2C0020D02B002020

+:10EFC000542E0020E82E0020342C0020502E00204B

+:10EFD000322C0020FC2E0020642E0020002F002068

+:10EFE000582E0020692E0020F02E0020182C002022

+:10EFF0005C2E002079EC010000810100D9E80100BD

+:10F0000065EC0100B42E0020CDEA0100282C002080

+:10F01000602E0020B9EC010008B5034B021C012151

+:10F020001888FFF725FC08BD322C0020F0B51F1C06

+:10F03000174B85B01B78051C0824002B24D00B1C13

+:10F040001449101C09782A1C002919D00F26802188

+:10F050006E443170104D1149009001970F68281CC3

+:10F06000311CB8473378201CDB0702D5281C01F07F

+:10F070004FFB33789B0706D5281C01F05FFB02E0AD

+:10F08000391C01F037FB041C201C05B0F0BDC04644

+:10F09000E82E0020502E0020292C0020542E002085

+:10F0A00008B5011C0148FCF77DFA08BD002F0020BF

+:10F0B000F0B501269E4B9FB00190DE6701F05CFB2E

+:10F0C00002300424052800D92EE1FFF743FE041E78

+:10F0D00000D029E110232422964F1E4302AB9B1837

+:10F0E0003D681E702C68201C02F0AAFA211C82B216

+:10F0F0000BA8FFF7B9FB041E0ED1A88CFFF7B0FBDD

+:10F10000041E09D1291C082226310EA802F00AFA91

+:10F110000EA8FFF7A7FB041C002C00D004E124215B

+:10F120003E6802AAB37D52181370B38A02AAD38430

+:10F13000338B0EAD1385738B211C5385281C402205

+:10F1400002F032FA7168002902D0281CFDF7A6FDF2

+:10F15000B168002902D014A8FDF7A0FDF1680029CC

+:10F1600002D016A8FDF79AFD3169002902D012A835

+:10F17000FDF794FD11220BABAB631DAB1A706D2331

+:10F18000002202A95B18281C1A70FCF74BFE041E13

+:10F1900000D0C9E005AE211C301C0C2202F004FA9C

+:10F1A000654B664873600123311CB4603380FCF703

+:10F1B000CDFB002803D0211C221CFBF7DFF90EAE8B

+:10F1C00000211422301C02F0EFF9012400235C4AD4

+:10F1D00034710E935378591E8B415B421340AB60E0

+:10F1E000FFF7C8FA10231A1C22436A731A1C224321

+:10F1F000AA7300221C432873291C52482C74EA73FA

+:10F20000FCF708F9041E00D08EE04F4E4F4B3278C9

+:10F210001968737838681B021343B2781E301204E1

+:10F220001A43F3781B061343494A00F053FC0028A5

+:10F2300079D13E68211C1C22281C02F0B5F9311C32

+:10F24000082226310BA802F06DF90BAB2B6080234E

+:10F250001B026B60F0231B03AB603E4B0326AB61CC

+:10F260000123281C5B422C746C612E73EB81FCF72C

+:10F2700093FA041E58D13868FFF7BEFC002852D11B

+:10F2800001F0BEF800284ED1019B002B4CD0201C71

+:10F2900000F048FC341C002846D000F0E3FC041EBB

+:10F2A000052C41D12C4E02ABDD1C307803A92A1C61

+:10F2B00000F04CFB002836D12B78002B03D1301CFA

+:10F2C000FEF76CF822E004AD281CFEF7FFF9337856

+:10F2D00000262B7000230C270893FF210BAB3A1C50

+:10F2E0000EA80A93AE70099702F05EF9281C08A9CF

+:10F2F000FEF740F9041E12D10BA80EA93A1C02F029

+:10F30000E7F800280BD1281CFEF72AF80123134A3E

+:10F31000041C1370124A11680B43136004E0013699

+:10F32000032ED7D100E0041C201C1FB0F0BDC04646

+:10F3300004E100E0EC2E002049EC0100B82E002092

+:10F34000582E0020002F0020B42E00201C2C00205E

+:10F35000E82E0020A9EC0100282C0020692E0020B6

+:10F36000F02E002008B500F061FF08BD73B506ACB3

+:10F370002678012903D1054D2C7801342C70079C87

+:10F380000096019400F0C2FF76BDC046FC2E00201E

+:10F390001FB50C4C0C4B2088984208D01321FFF766

+:10F3A0005DFA00280CD101235B42238008E0022390

+:10F3B00001A80370054B0C211A68F9F7ABFE002079

+:10F3C00004B010BD322C0020FFFF00001C2C0020D8

+:10F3D00030B585B06846FAF7C5FF041C011E3CD164

+:10F3E000009C002C09D001221D4B0C1C1A70042318

+:10F3F0001C4A1068034313602FE01B4D1B4B2888E9

+:10F4000098420BD01321FFF729FA002824D10123B9

+:10F4100001225B422B80164B1A701EE0FCF7F4F9B8

+:10F42000002819D1FFF718FA6846FAF79BFF002861

+:10F4300012D1009B002B08D00122094B1A70042323

+:10F44000084A11680B43136007E00D2301A80C2143

+:10F45000084A0370F9F75EFE041C201C05B030BD9D

+:10F46000692E0020F02E0020322C0020FFFF00002B

+:10F47000182C0020B1ED0100F0B5038885B0041C04

+:10F48000012B3DD15C4C2368002B00D1B1E05B4ED9

+:10F490005B4F2368EF211A1C0025FC3292683078FC

+:10F4A00091426D4113359A1839782B1C01F022F9DD

+:10F4B000002800D09DE02368132D1CD1FF33587223

+:10F4C000504B514D1A68012313702A1C2060FF32E3

+:10F4D000517A2A1C002905D185218900695C0029FF

+:10F4E00005D0181C86235B00584310182060236841

+:10F4F000002BCED17DE0FC339A6814329A60C8E7C5

+:10F50000FEF758F9201CFCF787F9201C01F0DCF805

+:10F51000201C00F04FFD3D48211CFBF752FF3C48EA

+:10F52000211CFCF769FB20881038092861D801F0FC

+:10F53000BBFF0F1A053B60603F596034052301A8EB

+:10F540000370E38883806389C380A38903814CE0CF

+:10F55000A288304B01A81A8000232F4A13702F4A2B

+:10F560001370012340E0012200242A4B52421A80EA

+:10F570002B4B1C702B4B1B78A34207D0FFF728FFA7

+:10F58000A04203D0211C221CFAF7F8FF00F060FD16

+:10F5900000282ED101A8022326E000F059FD002802

+:10F5A00027D101A806231FE001221D4B1A7020E07D

+:10F5B000A379012B04D0022B09D101A80A2301E071

+:10F5C000092301A80370184B0C211B689847174B9F

+:10F5D0001868FAF745FA00280BD000210A1CFAF740

+:10F5E000CDFF06E00B2301A803700F4B0C211B6815

+:10F5F000984705B0F0BDC046242C0020212C0020E7

+:10F60000182F0020F82E0020382C0020002F00207A

+:10F61000B82E0020322C0020E82E0020F42E0020EE

+:10F62000342C0020182C00201C2C00205C2E0020E4

+:10F6300008B5054B01201A88044B9A4202D0FFF707

+:10F64000A7FE002008BDC046322C0020FFFF0000AE

+:10F6500071DF7047F0B5041C8BB003AE301C019114

+:10F66000FEF734F82378002433700C23251C07AFF1

+:10F6700005930697301C04A9B470FDF77BFF0028A2

+:10F6800005D13B68AB4202D95A1C00D01D1C013485

+:10F69000E4B2032CEED10020019B01351D600BB0BC

+:10F6A000F0BD0000F0B5041C87B005AE301C02911F

+:10F6B0000392FEF70BF82378184D3370637800241B

+:10F6C000174F737001940C23AF606B60301C291CC2

+:10F6D000B470FDF74FFF002813D13B68019A9A429E

+:10F6E00000D20193029A93420BD9013B3B60301C3C

+:10F6F000291CFDF7D7FE002803D000210A1CFAF7C9

+:10F700003DFF0134E4B20C350C37032CDBD1002073

+:10F71000039B019A1A6007B0F0BDC046542F002029

+:10F72000BC2F00200C23F0B58578244F5D43791958

+:10F730004B60234B85B05B198B60041CFDF71AFFEF

+:10F740000197061E1E4F09D1E959201C03AAFFF795

+:10F75000A9FF00280CD0311C321C07E0201C03A993

+:10F76000FFF778FF002803D000210A1CFAF706FFF4

+:10F770000C22A078019B50431B183818114F124DD2

+:10F78000386098605A600430039E2B60FFF760FF7A

+:10F79000002803D000210A1CFAF7F0FE3B68296814

+:10F7A0001E60201CFDF77EFE002803D000210A1CED

+:10F7B000FAF7E4FE002005B0F0BDC0468C2F002013

+:10F7C000202F0020502F0020882F00200C23F0B580

+:10F7D00085782B4F5D4379194B602A4B87B05B19B5

+:10F7E0008B60041CFDF7C6FE0197061E254F43D112

+:10F7F000ED59201C291C03AAFFF754FF002803D051

+:10F80000311C321CFAF7BAFE039B00939D4233D0A1

+:10F810000C21A378019A4B431B4DD218FF181B4EA5

+:10F820005160976004A82A603760FFF711FF002835

+:10F8300003D000210A1CFAF7A1FE201C2968FDF75D

+:10F8400099FE002803D000210A1CFAF797FE3368BE

+:10F8500004A9181D072201F03BFE071E0CD13368D6

+:10F86000009A29681A60201CFDF71CFE002803D0AE

+:10F87000391C3A1CFAF782FE002007B0F0BDC046E2

+:10F880008C2F0020202F00201C2F0020842F0020F0

+:10F89000F0B5041C002687B004A8039301910292DE

+:10F8A000FDF714FF04AB274D274F1C706A46F3B2D7

+:10F8B0000093127804AB9A700C23AF606B6004A8BD

+:10F8C000291CFDF757FE041E34D13B68019A013B09

+:10F8D00093420ED10C235E431B4907228919043140

+:10F8E000029801F01FFE6A46039B1278201C1A70D2

+:10F8F00026E002331ED104A805A9FFF7ABFE0028BD

+:10F9000003D0211C221CFAF739FE381C059B08C0C5

+:10F91000FFF79EFE002803D000210A1CFAF72EFEF6

+:10F9200004A8291CFDF7BEFD002803D000210A1CF5

+:10F93000FAF724FE01360C350C37032EB6D105201C

+:10F9400007B0F0BD8C2F0020202F0020F0B5051C43

+:10F9500085B003AC201C0F1C0192FDF7B7FE25708B

+:10F960000024251C104EE3B200936B461B7803A8BD

+:10F9700083700C23736063430C4A311CD318B3604B

+:10F98000FDF7F8FD002805D16B461B7801353B706B

+:10F99000EDB2013701340C36032CE4D10020019B79

+:10F9A0001D7005B0F0BDC0468C2F0020202F002018

+:10F9B000F0B5041C85B003AE301C00910192FDF738

+:10F9C00085FE34700024154D154F0C23AF606B601D

+:10F9D000301C291CB470FDF7CDFD002814D13B6804

+:10F9E000009A013B934205D1019B301C1C70FDF72E

+:10F9F0001DFE07E0009A934206D93B60301C291C8B

+:10FA0000FDF750FD002806D10134E4B20C350C3767

+:10FA1000032CDAD1002005B0F0BDC0468C2F0020A9

+:10FA2000202F002070DF704771DF704773DF704751

+:10FA30000C23F0B589B001AC051C0393201C05AB69

+:10FA40000E1C0493FDF742FE002803D000210A1C7F

+:10FA5000FAF794FD257000270024002501A802A9CB

+:10FA60008770FDF787FD002815D105AB9A79D97904

+:10FA70001202090458790A43197A024309060A4313

+:10FA8000597A9B7A1B020B43AB4202D803D1A242A4

+:10FA900001D9141C1D1C0137FFB2032FDED1231C1A

+:10FAA00008202B4313D001220023A4185D41012319

+:10FAB000402200203370230AB370230CF370230E0E

+:10FAC000337152422B0A134374707571B37109B0CC

+:10FAD000F0BD000013B501930723002815D00029BD

+:10FAE00013D0002A11D00024094B18600079094B6B

+:10FAF0001870094B09481960094B04701A600130ED

+:10FB0000211C052201F050FD231C181C16BDC04607

+:10FB1000F02F0020FA2F0020EC2F0020F42F0020DF

+:10FB2000FC2F0020F7B54B4B051C18786B46DE1CEC

+:10FB300001A9321CFFF70AFF0124071E00D087E04D

+:10FB4000454B5C781878002C36D004282DD801F06D

+:10FB5000ABFC037E1B231B00404A1268D478002CA8

+:10FB600010D0317800293CD10CE0002D31D02A880A

+:10FB7000112A2ED13A4AA97912681278002A22D184

+:10FB8000132924D103223DE0002D5ED02A88192AB2

+:10FB90005DD1D97832784CE02C1E59D02A880024C7

+:10FBA000192A55D11C705C7052E0381C391C3A1C63

+:10FBB000FAF7E4FC3C1C4BE0042840D801F06AFC56

+:10FBC000D525313C2B00082942D104211970141C81

+:10FBD0003EE0224A12689478002C0BD0317800293C

+:10FBE00008D051780222002900D104221A70002284

+:10FBF000DA702DE031780029C4D051780122002933

+:10FC000000D104221A70012422E0002D20D02A887D

+:10FC1000192A1DD110E0002D1AD02A88192A17D1CF

+:10FC2000B0E7002D14D02A88192A11D10124D978DF

+:10FC3000327891420CD3A5E700221A7008E0201C0C

+:10FC4000211C221CFAF79AFC02E02C1C00E0002484

+:10FC5000201CFEBDFA2F0020F42F0020F02F0020E2

+:10FC6000FC2F0020F0B50026CA4BB1B09C781F1CB9

+:10FC7000B44200D09AE104A8FAF774FBB04203D072

+:10FC8000211C221CFAF77AFC049E002E04D00122CB

+:10FC9000C14B00261A7089E1311C18220EA801F010

+:10FCA00083FC311C10220AA801F07EFC311C0722C3

+:10FCB00006A801F079FCB94C02ABDD1C207803A941

+:10FCC0002A1CFFF743FE0094002800D059E1387841

+:10FCD000042837D801F0E8FB03070D2E1B00002095

+:10FCE000011C021C31E001232C783B71013CE4B281

+:10FCF0002EE07B78FC78002B03D12B78013B1C1B7A

+:10FD0000E4B201233B71FB780133FB7020E07B7888

+:10FD1000FC78002B03D12B78013B1C1BE4B20023A1

+:10FD200001223B71FB7801920133FB7063B2013316

+:10FD300013D14FE000233B71994B1B681C78002CBA

+:10FD400065D178E0301C311C321CFAF717FC71E0E9

+:10FD5000002263B2019201336CD02B7864B29C42D2

+:10FD600035DC1CAD281CFDF7B1FC0023AB70009BFB

+:10FD7000211C18786A2302AC24AA1B192870FFF7EB

+:10FD800087FD0025002812D002AB03331B789D426B

+:10FD900000D3F6E0814B02AC18786A2369B224AA3A

+:10FDA0001B19FFF775FD002801D00135ECE7002095

+:10FDB00024A9FFF737FE041E07D11CA806A9FDF7EA

+:10FDC00093FC002803D03C7104E0061CEEE0019B8C

+:10FDD000002B2FD02421082302AA52182C211370A3

+:10FDE00002AA5218137014AB0A936C481CAB0AA9F0

+:10FDF0000C93FDF75BFA012400281CD0C1E0002819

+:10FE000000D0BEE0211CFFF70DFE002807D0009BAC

+:10FE100024AC1878211CFFF70BFE0828EFD108232B

+:10FE20002370604B201C1A680C21F9F773F9002825

+:10FE300000D0A6E000243B79002B10D0012B00D08D

+:10FE40009DE0302102AA52181370042342217B71D5

+:10FE500006AB0F93002302AA521813804BE0002C2C

+:10FE600031D102AB03331B78022B13D924AD281CEC

+:10FE7000FDF72CFC8A22009B211C187802ABD218BB

+:10FE80002870FFF795FD00287BD1281CFDF768FA44

+:10FE9000002876D10AAB1A79002A74D11B7B002B7B

+:10FEA00071D1052338217B71002302AA5218402109

+:10FEB0001370A02302AA521842211380B42302AA6D

+:10FEC0005218138012E0042338217B71022302AA06

+:10FED000521840211370202302AA52184221138085

+:10FEE000052302AA521813800AAB11933021002374

+:10FEF00002AA521813707B7905A82B49042200939B

+:10FF000001F010FB08A8FFF78FFD002803D00021A7

+:10FF10000A1CFAF733FB002008A9FFF783FD00282D

+:10FF200003D000210A1CFAF729FB24AC0021201C75

+:10FF3000302201F039FB022323706B4601251B7828

+:10FF4000201CE370002105ABA57025826361FBF7DF

+:10FF5000FDF9002815D10EA8FFF768FD002810D183

+:10FF60003B78BD70032B21D1072323700D4B201C40

+:10FF70001A680C21F9F7CEF8002817D001E0032603

+:10FF800014E0061C12E00020011C021CFAF7F6FA2D

+:10FF900087E7C046F42F0020FB2F0020FA2F002017

+:10FFA000F02F0020EC2F0020E41C0200301C31B0A8

+:10FFB000F0BDC04610B50388112B09D0192B12D003

+:10FFC000102B1FD10022104B9A705A701A7019E032

+:10FFD000FFF7A8FD002815D0FFF744FE002811D038

+:10FFE00000210A1C0CE00024074B9C70FFF79AFDCF

+:10FFF000A04207D0FFF736FEA04203D0211C221CEE

+:020000022000DC

+:10000000FAF7BCFA10BDC046F42F002037B502380D

+:10001000012819D80D4B1A781D1C002A14D001A8EC

+:10002000FAF7A0F9002803D000210A1CFAF7A6FA73

+:10003000019C002C08D12C70FFF714FE002803D07F

+:10004000211C221CFAF79AFA37BDC046FB2F00206C

+:10005000014B98787047C046F42F0020FF23F0B57D

+:1000600085B001AC2371A37163711A4BBC270D1CC1

+:1000700019780023184E7F008B4208DA3A1C5A4345

+:10008000B218904201D1237101E00133F4E72379E2

+:10009000FF2B04D10020011C021CFAF76FFA002389

+:1000A000E3712B780195042B08D1EB79A371AB791F

+:1000B0006371EB68A3606B89E37105E0072B03D1E3

+:1000C000EB79A371AB796371044B201C1B689847D3

+:1000D00005B0F0BD003000200830002004300020C2

+:1000E00070B50F24012602784378A2433243857805

+:1000F0002240002901D0302100E02021A543A34364

+:1001000033432E43234034400A430B430C430270D5

+:100110004370847070BD0000F0B5066887B0072397

+:10012000002E6DD04468002C6AD0018A002967D067

+:100130008568002D64D0C268002A61D00423232A78

+:100140005ED9314B314F1E60314B324E1C60324B09

+:10015000324C1A800023324A2560324D11803B70A8

+:1001600033702B70837C9370C37CD370037D1079C4

+:10017000033B591E8B412C49DBB20B700F2102331C

+:1001800008401B010343DBB28B4301210B43137176

+:100190000023FF21537102A8082201F005FA20680C

+:1001A00002A9082201F094F9002802D03B7801331B

+:1001B0003B7004A8FF21082201F0F6F900235F1C20

+:1001C0002368F800181804A9082201F081F9002812

+:1001D00002D03378013333703B1E032BEFD1FF2164

+:1001E000042201A801F0E0F9206801A920300422CE

+:1001F00001F06EF90023984202D02A7801322A7069

+:10020000181C07B0F0BDC0460430002000300020AC

+:10021000883100208431002094310020803100207A

+:100220008E3100208C3100208D310020F0B587B058

+:1002300001900293019B0C1C03920720002B5DD0C0

+:1002400000295BD0029B002B58D000252C4F2D4E4F

+:100250003B789D4211DA3268EB00D3189A88A242AB

+:1002600009D119680198221C01F032F9002802D145

+:10027000029B1D7042E00135EAE737683D1C2435DA

+:10028000012B08D1BE88013EF217920F9619B610C5

+:100290000136B600AD191C492A19098804207918BD

+:1002A0008A422BD86946097B04AAD1719371948044

+:1002B000029A04A91370DB00F8180222049500F0DA

+:1002C0007BFF0F4B0E4A1B78013C013313708022D9

+:1002D000520513695169A4104B4301340198A200DF

+:1002E00082189A4204D90020011C021CFAF746F930

+:1002F000A2B2281C019900F05FFF002007B0F0BDFA

+:10030000003000208031002094310020F0B587B00B

+:1003100001930CAB1B78051C0293364B0E1C1B780B

+:10032000171C0720AB4263D90D9B002B60D0324ACB

+:10033000324B117800221B68C90059188B4209D032

+:10034000187AA84204D1587AB04201D10132D2B20F

+:100350000833F3E70D9B00211A7004A8082201F06E

+:1003600023F904AB1D70DF705E700D9B04AA1B782F

+:10037000214C9370131C6A46127921781A716A46CF

+:10038000127A03945A711D4B1A680023101C08300E

+:100390008B421DDA0478AC4217D1C478BC4214D128

+:1003A0000479A446019CA4450FD14479A446029C3B

+:1003B000A4450AD14478B44207D10133DB00D318F5

+:1003C0009B78FF2B04D00D9A10E001330830DFE753

+:1003D000042002290CD8481CC000101804A90222CD

+:1003E00000F0EAFE039B039A1B7801330020137090

+:1003F00007B0F0BD003000208431002080310020A3

+:10040000F0B5B1B0182100220CA801F0CDF81821E8

+:10041000002212A801F0C8F818A81821002201F043

+:10042000C3F800259F4B1B789D4200DB34E19E4BB7

+:1004300001221F68EB00FB18DC790026231C134007

+:100440000493069363083C1C1340059308340396F9

+:1004500002960196954B1B789E425CDA2378AB425C

+:1004600056D16378012B19D1019A6179D2000CA879

+:1004700011522179801881706278C270E178C27956

+:1004800019409A438A4B0A43C27119780430FFF726

+:1004900027FE019B0133DBB2019339E0022B1AD115

+:1004A0000121029B6279DB0012A81A522279C0183E

+:1004B00082706378C370E278C3790A408B43134338

+:1004C000C3717B4B04301978FFF70AFE029B01339E

+:1004D000DBB202931CE0032B00D0DFE00121039B81

+:1004E0006279DB0018A81A522279C01882706378EA

+:1004F000C370E278C3790A408B431343C3716C4BDA

+:1005000004301978FFF7ECFD039B0133DBB2039352

+:10051000013608349EE70024664B261C1B7809AA86

+:1005200000933B1C203300998C4210DA1978A942C1

+:100530000AD1012007906846B100007F5718B870B3

+:100540005F8801368F52F6B201340433EBE71EACFC

+:10055000201C0021482201F027F8574B524A1E93D5

+:10056000564B12681B6808A963606B461B7E2AA85D

+:1005700023726B461B7D63726B461B79A3720CABB7

+:10058000E3606B461B7A237412AB63616B461B7B83

+:10059000237618ABE361EB00D3189A88A2841B681A

+:1005A00008AA236209ABA3625F239B188E22DE7721

+:1005B00000235218137042490622A36300F0B2FFD1

+:1005C0003B4B012619780F232DA8027830249A433B

+:1005D00032431A40002900D1202422430270952280

+:1005E00008A8121810789843304303401C43147035

+:1005F0009622002008AC121910704842484103228C

+:1006000097240F230126121A08A824182078120113

+:1006100018401043C0B298433043207029A8047892

+:100620001C401443E4B29C433443852204700020F0

+:1006300008AC12191070049A824219D09E20001939

+:10064000027830249A4332431A40002900D12024F2

+:10065000224302709F2208A8121810789843304352

+:1006600003409B201C4308AB1470C018FFF738FDF3

+:10067000059B002B04D00E4B2EA81978FFF730FDF8

+:10068000BC20400068430F4B1EA91818FBF790FFD1

+:10069000002804D10135C5E6002000E0032031B078

+:1006A000F0BDC046003000208031002084310020A1

+:1006B0008D3100208C3100205D0002008831002047

+:1006C0008E31002008300020F8B5061C0024074DAC

+:1006D000074F2B789C4208D2BC2040006043311C5D

+:1006E0003818FBF721FE0134F3E7F8BD0030002095

+:1006F0000830002038B5BC24151C64006043034A50

+:1007000010189AB22B1CFCF715FA38BD08300020DF

+:1007100008B5084B021C1B780821002B08D0A72322

+:1007200005485B00C35A002B02D0FCF73DFA011CC0

+:10073000081C08BD003000200830002013B50C4A0A

+:10074000031C12780820002A10D0AF22094C520056

+:10075000A25A002A0AD00122002019789A569B78C2

+:10076000009001905BB2201CFCF74EFA16BDC0460B

+:100770000030002008300020F0B500240226144B81

+:10078000251C1A68134B111C1878B446131CC000A2

+:10079000101883420AD00127DE793E4200D03D1C6A

+:1007A00067463E4200D001240833F2E700200A4B9E

+:1007B0001B78DB00D318994206D04A7A0831013AF7

+:1007C000564272418018F6E740190019F0BDC04644

+:1007D000803100200030002084310020F7B5FDF783

+:1007E0002BFA031E4CD1191C1A1CFDF7ADFB01A8F6

+:1007F000FDF75CFB002808D0019B180AC0B200F08E

+:100800000BFD019BD8B200F007FD0024251C271C1E

+:10081000FDF7D4FA6B46061CD81C00F0E3FC002858

+:10082000F6D06B46DB78002D03D11C02371C012566

+:10083000EEE73A1C1332964202D91C02371CE7E756

+:100840001C433F23210AA20819401A400323A00B8E

+:1008500080B22340032B01D101235B42FDF774FBDF

+:1008600001A8FDF723FB0025A842D1D0019B180A5F

+:10087000C0B200F0D1FC019BD8B200F0CDFCC7E7BC

+:1008800001204042FEBD0000012310B509495C5C17

+:100890000A1C84420DD00133072BF8D10023D11854

+:1008A0004C780133002C02D14870D37101E0062B43

+:1008B000F5D110BDAC3100202FDF704732DF70471B

+:1008C00036DF704737DF704712DF7047002008B50A

+:1008D000011C054AF8F71EFC002803D000210A1C61

+:1008E000F9F74CFE08BDC046810B020008B504496B

+:1008F0000A2200F0EDFD43425841C0B208BDC04697

+:10090000CC3100200723012802D8024B18700023A5

+:10091000181C7047B631002070B5084E0820357895

+:10092000002D09D10124FDF70FFD201C3470FDF7C7

+:100930002FFD281C024B1C7070BDC0469931002051

+:100940009731002010B5054C2378002B05D0044BBF

+:100950001868F9F785F80023237010BDC031002016

+:10096000BC3100200723F7B5354A364901905C1E9B

+:10097000161C002C04D0023BD05CC854231CF6E7A4

+:10098000314F324D3B78101C391C2B70FDF75CFD4C

+:100990000097002852D03B782F78BB420AD13B1CED

+:1009A000284A013B04D3F05C995C8842F9D001E00D

+:1009B000002443E00025264C6570A570E570257184

+:1009C0006571A5712570E571BD4215DA009B1A7835

+:1009D000002393420EDA1B48F15C285C884207D161

+:1009E000031C2033DBB2072B04D9FFF74DFF01E0D6

+:1009F0000133EEE70135E7E70025009B1B789D42B8

+:100A000013DA705D031C2033DBB2072B07D80122F9

+:100A1000E0388240101C23781843207003E000283F

+:100A200001D0FFF731FF0135E7E7601C2178E2795B

+:100A3000FDF798FD019B20721C600124201CFEBD67

+:100A4000A43100209C3100209831002096310020F4

+:100A5000AC310020F0B5364B85B01B78002B64D04C

+:100A6000FDF7C6FC334C051E12D1334B33481A68D0

+:100A70000A21F8F74FFB002803D0291C2A1CF9F79C

+:100A80007DFDFFF75FFF0020FDF782FC00234BE0B8

+:100A90002378002B49D103A8FFF764FF002844D036

+:100AA000274B1B78002B2FD103990A26CA79081CE3

+:100AB0000192302208279446421A019DD2B2AA42DE

+:100AC00021D24278151C1E3D092D09D85D1CCB187A

+:100AD000272A01D1624600E013325A702B1C10E025

+:100AE000151F192D03D85D32CD186A7008E02A2A27

+:100AF00002D1CA18577003E0282A02D1CA185670CA

+:100B000001330130D8E7CB710B4B03981B680A21E6

+:100B100098470398FFF7EAFE002806D0FFF712FF78

+:100B20000120FDF735FC0123237005B0F0BDC04660

+:100B30009931002097310020B8310020C2310020C7

+:100B4000B631002070B50C4B1C781E1C002C11D146

+:100B5000051C201CFFF77EFF084B291C1868221C6F

+:100B6000F8F728FF002803D0211C221CF9F706FD06

+:100B700001233370002070BDC0310020BC31002043

+:100B800008B5074B1B78002B05D00020FDF700FCB3

+:100B90000022044B1A70F6204000FFF7D3FF08BD77

+:100BA000993100209731002070B5072401283AD8E8

+:100BB000002938D01D4E08243578002D33D11C4B28

+:100BC000062218701B4B1C4819601C4B291C1D70F9

+:100BD0001B4B1D7000F0E8FC291C0622194800F090

+:100BE000E3FC291C0A22184800F0DEFC291C012520

+:100BF000164C0A22201C00F0D7FC1548291C154A67

+:100C00006572F8F7B1FE041E0DD1FDF737FD041E25

+:100C100009D104201049FDF767FD041E03D1211CF2

+:100C2000FFF7AEFF3570201C70BDC046C1310020FB

+:100C3000B6310020B8310020A431002098310020C6

+:100C4000963100209C310020CC310020C2310020A0

+:100C5000BC310020550A0200CD08020008B5FDF79E

+:100C6000C7FB08BD73B56B4600249E1DDD1D301CFF

+:100C700034702C70FFF728FEA04203D0211C221CE8

+:100C8000F9F77CFC3378012B0ED1281CFFF718FEF6

+:100C9000FFF758FE0620FFF70FFE0620FFF70EFEB7

+:100CA0002878FFF70FFE06E04023074A0749D3677D

+:100CB000C02252008B50FDF79BFB002801D0FDF7AE

+:100CC000A1FB012076BDC04604E100E000E100E0A8

+:100CD0003CDF704749DF70474ADF70474BDF7047A2

+:100CE00048DF704712DF704713B56B46DC1D0023E9

+:100CF000201C2370FFF7F6FF2378002B04D18023FC

+:100D00000122DB055A6001E0FFF7E6FF13BD00009A

+:100D1000F0B580260124214BF605F55885B0254015

+:100D2000019338D102ABDF1C0090381CDD70FFF757

+:100D3000D9FF3A78002A21D1194B5A603460009AC1

+:100D4000002A28D0221C5D68002D24D1E1208000DB

+:100D50000138C046C046C046C046C046C046C04630

+:100D6000C046C046C046C046C046F1D10199715840

+:100D70002142E8D18021C9050A60E4E7FFF7AAFF14

+:100D8000009B002B07D00395039B002B03D103A8E6

+:100D9000FFF7A4FFF8E705B0F0BDC0460C04000063

+:100DA000FC00004070B5C22380251822ED059B0091

+:100DB000EA5000230126284C284A23616361AA587F

+:100DC000324203D1301CFFF7A3FF331C01222A61FA

+:100DD0002269002AFCD000211F4A11618B4201D0F8

+:100DE000FFF782FF8023C1221821DB059200995072

+:100DF0001B4A20201168A722D20099500138C04612

+:100E0000C046C046C046C046C046C046C046C046B2

+:100E1000C046C046C046F1D1012120209961013869

+:100E2000C046C046C046C046C046C046C046C04692

+:100E3000C046C046C046C046F1D1C020FF24094A82

+:100E4000596180001358A343C02423431350C02387

+:100E50005B00D150116070BDFC0000400C0400002C

+:100E60002820002000E100E037B5214A214913681D

+:100E7000096801331360151C01AC00208B4208D1B6

+:100E8000012323701C4B60701A68201C0221F8F7A4

+:100E900041F91A4B2A681B689A4209D30023237030

+:100EA00001236370144B201C1A680221F8F732F9F1

+:100EB000002803D000210A1CF9F760FB0023201C46

+:100EC0002370FFF70FFF217800290FD10C4B0D4C39

+:100ED0001A68207801321A60002802D01960217047

+:100EE00004E0092A02D91860FFF75CFF37BDC0464D

+:100EF0000832002020200020DC31002024200020A7

+:100F0000F8310020FC31002070B580262C4CF6050D

+:100F10006369002B19D00023202063610138C0468B

+:100F2000C046C046C046C046C046C046C046C04691

+:100F3000C046C046C046F1D101257561281CFFF7A7

+:100F4000E7FE204BB56735611D702369002B35D056

+:100F500020200138C046C046C046C046C046C046F4

+:100F6000C046C046C046C046C046C046F1D120205B

+:100F70000138C046C046C046C046C046C046C0460E

+:100F8000C046C046C046C046C046F1D12020013808

+:100F9000C046C046C046C046C046C046C046C04621

+:100FA000C046C046C046C046F1D10022044B1A617B

+:100FB000FFF79AFE80230122DB05DA6770BDC04689

+:100FC000FC000040FC31002037B56B46DD1D0023DE

+:100FD0002B70984201D107244CE00029FBD0264A0F

+:100FE000041C1160011C254A18311360026A244B4D

+:100FF0001A60244B426A24481A60082200F092FAD0

+:1010000018222248211C00F08DFAA36A5A1E7E2A5B

+:10101000E1D81F4A281C1360FFF764FE2B78002BD1

+:1010200016D180228323D205DB00D15880235B02B6

+:101030000B400BD1A321C900535001239360154AE3

+:101040009368002BFCD00022124B9A60FFF7AAFE97

+:10105000114D0121281C114AF8F786FC041E09D104

+:101060000120FFF755FE8021221C28680902F8F7AD

+:10107000A1FC041C201C3EBDDC31002008320020F5

+:10108000242000202020002000320020E031002019

+:1010900028200020FC000040D8310020690E02000A

+:1010A0000022024B1A60024B186070470832002081

+:1010B0002420002008B5FFF713FE022803D120BF2B

+:1010C00040BF20BF06E0002804D00020011C021C05

+:1010D000F9F754FA08BD000038B50024064D2B7806

+:1010E0009C4205DAA300EB185B6898470134F6E7E9

+:1010F000FFF7FAFD38BDC04600320020F7B56B4659

+:1011000000240126DD1D114F2C703B789C420ADA29

+:10111000A300FB185B6801209847431E984140429A

+:1011200006400134F1E7002E0ED0281CFFF7DAFD4F

+:101130002B78002B02D0FFF7CBFD05E0A023802207

+:101140000121DB00D205D150F7BDC046E0310020BF

+:10115000002008B5024B1860FFF7DAFD08BDC04655

+:101160000832002048DF704770B5041C0D1C0F4E7C

+:101170003378002B02D0FFF7F5FFF9E70121802635

+:101180000B4B0C4AF600995080209959C000002959

+:10119000FAD0256019580029FCD000200449885055

+:1011A0008021C9005A58002AFCD070BD0C320020A2

+:1011B00000E001400405000070B5051C0E1C920003

+:1011C0008418A54205D0281C02CEFFF7CDFF0435B8

+:1011D000F7E7002070BD0000014B18707047C04653

+:1011E0000E32002010B5094B1B78002B0CD008499B

+:1011F0000022CC68012C06D1CA60A3220549D20086

+:101200008A58027000E0131C181C10BD0D3200201B

+:10121000FC20004000200040074B1B78002B09D029

+:10122000064A074BD050074A136A012BFCD1002213

+:10123000044B1A627047C0460D3200200020004067

+:101240001C050000FC20004010B5094B041C1B7855

+:101250000078002B0AD0074B1B78002B06D10134F5

+:10126000002803D0FFF7D8FF2078F8E710BDC0466C

+:101270000D3200200E3200201FB5064B1B78002BCC

+:1012800006D001A9102200F059FA01A8FFF7DCFFEF

+:1012900005B000BD0D3200201FB5064B1B78002B9A

+:1012A00006D001A90A2200F049FA01A8FFF7CCFFF5

+:1012B00005B000BD0D320020F7B59C4608AB1B7889

+:1012C0002B4C01932B4B1D682B4B9D4204D0A0232C

+:1012D0000022DB00E2504AE00E1CA0250323C136A9

+:1012E000FF36ED05B600735166460027C136FF365E

+:1012F000B6007751214EA1516646214966500199A9

+:10130000B94212D0011CC131FF3189004B51131C6D

+:10131000C133FF339B005F51A223DB00E250A123C6

+:101320000122DB00E050174BE250174A174B012512

+:10133000E250A0230422DB00E250FA200022144BEA

+:10134000A56080002560DA600138C046C046C0460E

+:10135000C046C046C046C046C046C046C046C0465D

+:10136000C046F1D10B48FFF76FFF0B4B1D70F7BD67

+:101370000020004000970300554152540C05000026

+:10138000140500006C05000000E0D70124050000F2

+:10139000FC200040E81C02000D32002003210F4811

+:1013A00002680A4302600E4802680A4302600D495F

+:1013B0000D4A0E4B9B1A03DD043BC858D050FBDC92

+:1013C00000200B490B4A086004319142FBDB0A48BC

+:1013D00085460A4880470A48004700002405004027

+:1013E00054050040301D0200002000202C20002069

+:1013F0002C200020583200200040002015E1010080

+:1014000001AE0100FEE7FEE7FEE7FEE7FEE7FEE7CE

+:10141000002804DA4042002904DA494220E0002989

+:101420001EDA494200B500F01BF8404200BD00281A

+:101430000EDA4042002905DA494200B500F010F802

+:10144000494200BD00B500F00BF84042494200BDE2

+:10145000002905DA494200B500F002F8404200BD1B

+:10146000002900D114E001220023884206D900316E

+:1014700004D45200490001D48842FAD8884201D3EA

+:10148000401A134349085208F8D1014618467047DC

+:10149000FFF7FEFF02B471464908490009564900AA

+:1014A0008E4402BC7047C04602B4714649084900E8

+:1014B000095C49008E4402BC7047C04603B47146C3

+:1014C000490840004900095A49008E4403BC70474E

+:1014D00030B5032A20D9031C0B439B0713D0047893

+:1014E0000D784B1C8A18AC4206D018E00133591E07

+:1014F00004780D78AC4212D101309342F6D100202D

+:1015000030BC02BC084703680C68A34204D1043A0B

+:1015100004300431032AF6D8002AE0D1EFE7601B3B

+:10152000EEE7C046F0B50F2A36D9031C0B439B07E4

+:1015300036D1151C031C103D2D0901352D014C1908

+:101540000E681E604E685E608E6810319E600E1FD1

+:10155000376810331E1F3760A142F1D10F234519A0

+:101560001340032B1ED90021043B9B0801339B0031

+:1015700066586E5004319942FAD103236D181A400F

+:101580006118002A05D00023CC5CEC54013393424F

+:10159000FAD1F0BC02BC0847051C002AF3D1F8E7D9

+:1015A000051CF0E71A1CF8E770B5830742D0541EFB

+:1015B000002A3CD00D062D0E031C032603E0621EFC

+:1015C000002C34D0141C01335A1E15703342F6D14E

+:1015D000032C23D9FF220A4015022A4315042A436B

+:1015E0000F2C2AD9251C103D2D0901352D015D191F

+:1015F0001A605A609A6010331E1F32609D42F7D104

+:101600000F263440032C09D9231F9B0801339B006C

+:10161000EB1804C5AB42FCD103221440002C06D0C9

+:1016200009061C19090E19700133A342FBD170BCC5

+:1016300002BC0847141C031CCAE71D1CE4E7C04693

+:1016400010B5830729D00378002B28D0031C032171

+:1016500002E01A78002A1CD001330B42F9D1196834

+:10166000104A114C8A188A4322420FD10433196858

+:101670000C4A8A188A43224208D104331968094A5D

+:101680008A188A432242F1D000E001331A78002AF6

+:10169000FBD1181A10BC02BC0847031CDFE700206E

+:1016A000F8E7C046FFFEFEFE80808080F0B557461A

+:1016B00045464E46E0B48AB090466A461E4B0D1C25

+:1016C000061C13CB13C213CB13C213CB13C21B784C

+:1016D00013704346023BEA462F1C0024222B01D9FB

+:1016E00026E04C46631C301C41469946FFF7B8FE85

+:1016F00053465B5C301C3B704146FFF7B1FE01373F

+:10170000061EEED14B46E8540023002C07D0EA5CBD

+:10171000295DE9542A550133013CA342F7DB281C1B

+:101720000AB01CBC90469946A246F0BC02BC0847D1

+:101730002C700020F4E7C046001D020008B5FFF73A

+:10174000B5FF08BC02BC0847F8B5C046F8BC08BCE9

+:101750009E467047F8B5C046F8BC08BC9E46704728

+:101760000D0A4164647220000D0A4261747465724E

+:10177000792070657263656E742063686172676555

+:1017800020000D0A456E746572696E672042696EAD

+:10179000617279206D6F646520286E6F206D6F72A5

+:1017A0006520737472696E677320666F7220796F3B

+:1017B00075290D0A000D0A61202D2061646472658F

+:1017C00073732C2062202D20626174746572792CF1

+:1017D0002064202D206465627567206164647265F1

+:1017E00073732C2068202D2068656C702C20692074

+:1017F0002D2049442C000D0A6B202D20746F676743

+:101800006C65206B65796C6F6767696E672C206CFF

+:10181000202D206C69642073746174652C20722003

+:101820002D20726573657420656E61626C652C2075

+:1018300076202D2076657273696F6E0D0A000D0A91

+:10184000494420006B65797374726F6B65206C6F0F

+:101850006767696E67200064697361626C65640D17

+:101860000A00656E61626C65640D0A000D0A4C69C0

+:101870006420006E6F74200070726573656E740072

+:10188000526573657420656E61626C65642C2068B6

+:101890006F6C64207265736574206C6F7720746F51

+:1018A0002065786974206465627567206D6F646572

+:1018B0002E0D0A000D0A56657273696F6E200020A6

+:1018C0004275696C74206F6E20004E6F7620313146

+:1018D000203230313500206174200030363A333008

+:1018E0003A35360020707265737365640D0A002105

+:1018F00021212121204150505F4552524F525F48D3

+:10190000414E444C45522021212121206572726FA5

+:10191000725F636F6465203D20307800696E20003F

+:10192000204020004520000D0A5570646174652137

+:101930000D0A00504149520D0A00706169720D0A8A

+:10194000004C6964206E6F742070726573656E74EC

+:101950000D0A004D6F64696669657220427974658D

+:101960003A20307800204E756D204B6579733A200F

+:1019700000205363616E20436F646573202868659F

+:1019800078293A200062460D0A00506978656C207B

+:1019900043204B6579626F61726400476F6F676CBB

+:1019A0006500302E313531313034004572726F723E

+:1019B000200020747279696E6720746F2000557062

+:1019C0006461746520746865206C6576656C20744C

+:1019D0006F2000436F6E0D0A004665617475726575

+:1019E00020004F757470757420400020646174612C

+:1019F000200020696E6465782000206C656E2000F0

+:101A0000206964782000436F6E6E205570646174A5

+:101A1000653A20696E74657276616C20002C2073C3

+:101A20006C617665206C6174656E637920002C2092

+:101A30007375706572766973696F6E2074696D6510

+:101A40006F75742000416476420D0A00416464722F

+:101A5000204368616E676564200D0A0057616974F0

+:101A6000696E6720666F7220706173736B65790DA4

+:101A70000A004F4F424B657920526571210D0A00D3

+:101A8000506173736B65792072656365697665640F

+:101A90000D0A00526561642052657120004469732B

+:101AA000636F6E6E0D0A0044697361626C65640D4C

+:101AB0000A004164766572746973652061676169C3

+:101AC0006E0D0A00506F776572204F66660D0A0032

+:101AD00054696D656F75740D0A00537461727420DA

+:101AE0004446550D0A0045767420005265736574AE

+:101AF00020526561736F6E203D20307800636F6DFA

+:101B00007320696E6974200D0A0070777220696E07

+:101B100069740D0A0072656164793E20000000005E

+:101B200005010906A101050719E029E715002501AE

+:101B30007501950881029501750881019506750862

+:101B400015002565050719002965810095057501B2

+:101B500005081901290591029501750391010905EF

+:101B6000150026FF0075089502B102C077696C6CFC

+:101B70002074727920616761696E206C617465728E

+:101B80000D0A006D5F7570646174655F636F756EDB

+:101B9000742000706172616D657465722065727287

+:101BA0006F7220636F64652000646973636F6E6E8B

+:101BB000656374206572726F7220636F64652000C4

+:101BC00023D1BCEA5F782315DEEF1212000000007B

+:101BD000C7CC010069CE0100CBCC010025CF0100AC

+:101BE000CFCC01002DCF0100D3CC010061CF01008B

+:101BF000D7CC01009DCD0100DBCC010061CE0100FE

+:101C0000FFFFFFFFFFC1FBE84C90728BE7B35189E8

+:101C100063AB232302841872AA612F3B51A8E537D6

+:101C200049FBC9CA0C18532CFD0000000000E0005D

+:101C300000000000E3002B00041D1E140000000043

+:101C400007062008050A17220919211500000000BF

+:101C5000161B1F1A000000000E36250C110B1C234A

+:101C60000D1024180000000000E100E52E34002DC6

+:101C700033382713000000000F372612E60000005B

+:101C80000000E200002A0000282C51520000000051

+:101C900000004F50020A07050309081B12100C0F21

+:101CA0000B0000000D0A4261747465727920766F32

+:101CB0006C7461676520002075560D0A00000000F5

+:101CC0006456463C090600000B794100C06A3E009C

+:101CD000AEAF3B00B9943A00B8A73700EB1137001C

+:101CE000C29F3300121801000D0A55415254206959

+:101CF0006E697469616C697A65640D0A00000000A0

+:101D00003031323334353637383961626364656671

+:101D10006768696A6B6C6D6E6F70717273747576DB

+:101D20007778797A0000000074F6FF7F01000000E8

+:101D30004B42000000000000000000000000000016

+:101D400000000000008000400800000004000000C7

+:101D5000FFFFFFFFFFFFFFFF20000000FFFFFFFF6F

+:101D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83

+:101D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73

+:101D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63

+:101D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53

+:101DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43

+:101DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33

+:101DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23

+:101DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13

+:101DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03

+:101DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3

+:101E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2

+:101E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2

+:101E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2

+:101E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2

+:101E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2

+:101E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92

+:101E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82

+:101E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72

+:101E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62

+:101E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52

+:101EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42

+:101EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32

+:101EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22

+:101ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12

+:101EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02

+:101EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2

+:101F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1

+:101F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1

+:101F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1

+:101F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1

+:101F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1

+:101F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91

+:101F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81

+:101F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71

+:101F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61

+:101F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51

+:101FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41

+:101FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31

+:101FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21

+:101FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11

+:101FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01

+:101FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1

+:10200000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0

+:10201000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0

+:10202000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0

+:10203000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0

+:10204000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0

+:10205000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

+:10206000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80

+:10207000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70

+:10208000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60

+:10209000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50

+:1020A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

+:1020B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30

+:1020C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20

+:1020D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10

+:1020E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00

+:1020F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

+:10210000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF

+:10211000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF

+:10212000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF

+:10213000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF

+:10214000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F

+:10215000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F

+:10216000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F

+:10217000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F

+:10218000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F

+:10219000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F

+:1021A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F

+:1021B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F

+:1021C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F

+:1021D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F

+:1021E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+:1021F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF

+:10220000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE

+:10221000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE

+:10222000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE

+:10223000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE

+:10224000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E

+:10225000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E

+:10226000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E

+:10227000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E

+:10228000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E

+:10229000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E

+:1022A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E

+:1022B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E

+:1022C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E

+:1022D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E

+:1022E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE

+:1022F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE

+:10230000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD

+:10231000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD

+:10232000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD

+:10233000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD

+:10234000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D

+:10235000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D

+:10236000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D

+:10237000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D

+:10238000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D

+:10239000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D

+:1023A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D

+:1023B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D

+:1023C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D

+:1023D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D

+:1023E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD

+:1023F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED

+:10240000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC

+:10241000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC

+:10242000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC

+:10243000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC

+:10244000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C

+:10245000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C

+:10246000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C

+:10247000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C

+:10248000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C

+:10249000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C

+:1024A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C

+:1024B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C

+:1024C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C

+:1024D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C

+:1024E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC

+:1024F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC

+:10250000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB

+:10251000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB

+:10252000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB

+:10253000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB

+:10254000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B

+:10255000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B

+:10256000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B

+:10257000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B

+:10258000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B

+:10259000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B

+:1025A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B

+:1025B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B

+:1025C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B

+:1025D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B

+:1025E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB

+:1025F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB

+:10260000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA

+:10261000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA

+:10262000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA

+:10263000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA

+:10264000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A

+:10265000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A

+:10266000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A

+:10267000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A

+:10268000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A

+:10269000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A

+:1026A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A

+:1026B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A

+:1026C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A

+:1026D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A

+:1026E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA

+:1026F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA

+:10270000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9

+:10271000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9

+:10272000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9

+:10273000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9

+:10274000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99

+:10275000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89

+:10276000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79

+:10277000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69

+:10278000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59

+:10279000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49

+:1027A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39

+:1027B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29

+:1027C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19

+:1027D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09

+:1027E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9

+:1027F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9

+:10280000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8

+:10281000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8

+:10282000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8

+:10283000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8

+:10284000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98

+:10285000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88

+:10286000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78

+:10287000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68

+:10288000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58

+:10289000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48

+:1028A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38

+:1028B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28

+:1028C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18

+:1028D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08

+:1028E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8

+:1028F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8

+:10290000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7

+:10291000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7

+:10292000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7

+:10293000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7

+:10294000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97

+:10295000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87

+:10296000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77

+:10297000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67

+:10298000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57

+:10299000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47

+:1029A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37

+:1029B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27

+:1029C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17

+:1029D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07

+:1029E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7

+:1029F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7

+:102A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6

+:102A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6

+:102A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6

+:102A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6

+:102A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96

+:102A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86

+:102A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76

+:102A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66

+:102A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56

+:102A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46

+:102AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36

+:102AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26

+:102AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16

+:102AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06

+:102AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6

+:102AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6

+:102B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5

+:102B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5

+:102B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5

+:102B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5

+:102B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95

+:102B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85

+:102B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75

+:102B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65

+:102B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55

+:102B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45

+:102BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35

+:102BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25

+:102BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15

+:102BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05

+:102BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5

+:102BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5

+:102C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4

+:102C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4

+:102C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4

+:102C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4

+:102C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94

+:102C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84

+:102C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74

+:102C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64

+:102C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54

+:102C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44

+:102CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34

+:102CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24

+:102CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14

+:102CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04

+:102CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4

+:102CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4

+:102D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3

+:102D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3

+:102D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3

+:102D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3

+:102D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93

+:102D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83

+:102D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73

+:102D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63

+:102D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53

+:102D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43

+:102DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33

+:102DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23

+:102DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13

+:102DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03

+:102DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3

+:102DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3

+:102E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2

+:102E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2

+:102E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2

+:102E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2

+:102E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92

+:102E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82

+:102E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72

+:102E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62

+:102E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52

+:102E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42

+:102EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32

+:102EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22

+:102EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12

+:102ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02

+:102EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2

+:102EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2

+:102F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1

+:102F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1

+:102F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1

+:102F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1

+:102F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91

+:102F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81

+:102F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71

+:102F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61

+:102F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51

+:102F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41

+:102FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31

+:102FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21

+:102FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11

+:102FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01

+:102FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1

+:102FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1

+:10300000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0

+:10301000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0

+:10302000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0

+:10303000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0

+:10304000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

+:10305000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80

+:10306000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70

+:10307000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60

+:10308000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50

+:10309000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

+:1030A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30

+:1030B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20

+:1030C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10

+:1030D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00

+:1030E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

+:1030F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0

+:10310000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF

+:10311000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF

+:10312000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF

+:10313000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F

+:10314000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F

+:10315000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F

+:10316000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F

+:10317000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F

+:10318000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F

+:10319000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F

+:1031A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F

+:1031B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F

+:1031C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F

+:1031D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+:1031E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF

+:1031F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF

+:10320000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE

+:10321000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE

+:10322000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE

+:10323000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E

+:10324000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E

+:10325000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E

+:10326000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E

+:10327000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E

+:10328000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E

+:10329000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E

+:1032A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E

+:1032B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E

+:1032C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E

+:1032D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE

+:1032E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE

+:1032F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE

+:10330000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD

+:10331000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD

+:10332000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD

+:10333000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D

+:10334000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D

+:10335000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D

+:10336000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D

+:10337000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D

+:10338000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D

+:10339000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D

+:1033A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D

+:1033B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D

+:1033C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D

+:1033D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD

+:1033E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED

+:1033F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD

+:10340000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC

+:10341000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC

+:10342000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC

+:10343000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C

+:10344000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C

+:10345000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C

+:10346000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C

+:10347000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C

+:10348000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C

+:10349000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C

+:1034A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C

+:1034B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C

+:1034C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C

+:1034D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC

+:1034E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC

+:1034F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC

+:10350000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB

+:10351000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB

+:10352000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB

+:10353000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B

+:10354000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B

+:10355000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B

+:10356000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B

+:10357000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B

+:10358000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B

+:10359000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B

+:1035A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B

+:1035B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B

+:1035C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B

+:1035D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB

+:1035E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB

+:1035F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB

+:10360000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA

+:10361000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA

+:10362000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA

+:10363000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A

+:10364000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A

+:10365000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A

+:10366000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A

+:10367000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A

+:10368000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A

+:10369000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A

+:1036A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A

+:1036B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A

+:1036C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A

+:1036D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA

+:1036E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA

+:1036F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA

+:10370000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9

+:10371000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9

+:10372000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9

+:10373000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99

+:10374000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89

+:10375000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79

+:10376000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69

+:10377000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59

+:10378000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49

+:10379000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39

+:1037A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29

+:1037B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19

+:1037C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09

+:1037D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9

+:1037E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9

+:1037F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9

+:10380000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8

+:10381000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8

+:10382000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8

+:10383000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98

+:10384000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88

+:10385000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78

+:10386000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68

+:10387000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58

+:10388000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48

+:10389000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38

+:1038A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28

+:1038B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18

+:1038C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08

+:1038D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8

+:1038E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8

+:1038F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8

+:10390000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7

+:10391000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7

+:10392000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7

+:10393000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97

+:10394000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87

+:10395000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77

+:10396000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67

+:10397000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57

+:10398000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47

+:10399000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37

+:1039A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27

+:1039B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17

+:1039C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07

+:1039D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7

+:1039E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7

+:1039F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7

+:103A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6

+:103A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6

+:103A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6

+:103A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96

+:103A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86

+:103A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76

+:103A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66

+:103A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56

+:103A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46

+:103A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36

+:103AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26

+:103AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16

+:103AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06

+:103AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6

+:103AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6

+:103AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6

+:103B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5

+:103B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5

+:103B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5

+:103B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95

+:103B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85

+:103B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75

+:103B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65

+:103B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55

+:103B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45

+:103B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35

+:103BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25

+:103BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15

+:103BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05

+:103BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5

+:103BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5

+:103BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5

+:103C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4

+:103C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4

+:103C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4

+:103C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94

+:103C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84

+:103C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74

+:103C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64

+:103C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54

+:103C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44

+:103C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34

+:103CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24

+:103CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14

+:103CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04

+:103CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4

+:103CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4

+:103CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4

+:103D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3

+:103D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3

+:103D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3

+:103D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93

+:103D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83

+:103D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73

+:103D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63

+:103D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53

+:103D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43

+:103D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33

+:103DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23

+:103DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13

+:103DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03

+:103DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3

+:103DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3

+:103DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3

+:103E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2

+:103E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2

+:103E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2

+:103E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92

+:103E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82

+:103E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72

+:103E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62

+:103E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52

+:103E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42

+:103E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32

+:103EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22

+:103EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12

+:103EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02

+:103ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2

+:103EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2

+:103EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2

+:103F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1

+:103F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1

+:103F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1

+:103F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91

+:103F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81

+:103F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71

+:103F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61

+:103F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51

+:103F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41

+:103F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31

+:103FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21

+:103FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11

+:103FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01

+:103FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1

+:103FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1

+:103FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1

+:10400000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0

+:10401000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0

+:10402000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0

+:10403000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

+:10404000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80

+:10405000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70

+:10406000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60

+:10407000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50

+:10408000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

+:10409000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30

+:1040A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20

+:1040B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10

+:1040C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00

+:1040D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

+:1040E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0

+:1040F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0

+:10410000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF

+:10411000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF

+:10412000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F

+:10413000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F

+:10414000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F

+:10415000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F

+:10416000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F

+:10417000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F

+:10418000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F

+:10419000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F

+:1041A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F

+:1041B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F

+:1041C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+:1041D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF

+:1041E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF

+:1041F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF

+:10420000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE

+:10421000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE

+:10422000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E

+:10423000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E

+:10424000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E

+:10425000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E

+:10426000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E

+:10427000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E

+:10428000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E

+:10429000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E

+:1042A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E

+:1042B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E

+:1042C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE

+:1042D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE

+:1042E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE

+:1042F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE

+:10430000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD

+:10431000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD

+:10432000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D

+:10433000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D

+:10434000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D

+:10435000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D

+:10436000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D

+:10437000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D

+:10438000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D

+:10439000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D

+:1043A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D

+:1043B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D

+:1043C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD

+:1043D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED

+:1043E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD

+:1043F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD

+:10440000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC

+:10441000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC

+:10442000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C

+:10443000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C

+:10444000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C

+:10445000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C

+:10446000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C

+:10447000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C

+:10448000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C

+:10449000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C

+:1044A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C

+:1044B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C

+:1044C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC

+:1044D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC

+:1044E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC

+:1044F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC

+:10450000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB

+:10451000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB

+:10452000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B

+:10453000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B

+:10454000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B

+:10455000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B

+:10456000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B

+:10457000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B

+:10458000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B

+:10459000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B

+:1045A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B

+:1045B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B

+:1045C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB

+:1045D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB

+:1045E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB

+:1045F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB

+:10460000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA

+:10461000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA

+:10462000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A

+:10463000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A

+:10464000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A

+:10465000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A

+:10466000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A

+:10467000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A

+:10468000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A

+:10469000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A

+:1046A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A

+:1046B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A

+:1046C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA

+:1046D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA

+:1046E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA

+:1046F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA

+:10470000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9

+:10471000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9

+:10472000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99

+:10473000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89

+:10474000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79

+:10475000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69

+:10476000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59

+:10477000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49

+:10478000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39

+:10479000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29

+:1047A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19

+:1047B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09

+:1047C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9

+:1047D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9

+:1047E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9

+:1047F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9

+:10480000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8

+:10481000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8

+:10482000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98

+:10483000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88

+:10484000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78

+:10485000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68

+:10486000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58

+:10487000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48

+:10488000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38

+:10489000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28

+:1048A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18

+:1048B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08

+:1048C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8

+:1048D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8

+:1048E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8

+:1048F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8

+:10490000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7

+:10491000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7

+:10492000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97

+:10493000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87

+:10494000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77

+:10495000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67

+:10496000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57

+:10497000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47

+:10498000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37

+:10499000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27

+:1049A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17

+:1049B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07

+:1049C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7

+:1049D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7

+:1049E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7

+:1049F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7

+:104A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6

+:104A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6

+:104A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96

+:104A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86

+:104A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76

+:104A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66

+:104A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56

+:104A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46

+:104A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36

+:104A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26

+:104AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16

+:104AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06

+:104AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6

+:104AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6

+:104AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6

+:104AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6

+:104B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5

+:104B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5

+:104B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95

+:104B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85

+:104B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75

+:104B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65

+:104B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55

+:104B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45

+:104B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35

+:104B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25

+:104BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15

+:104BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05

+:104BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5

+:104BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5

+:104BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5

+:104BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5

+:104C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4

+:104C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4

+:104C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94

+:104C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84

+:104C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74

+:104C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64

+:104C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54

+:104C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44

+:104C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34

+:104C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24

+:104CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14

+:104CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04

+:104CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4

+:104CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4

+:104CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4

+:104CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4

+:104D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3

+:104D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3

+:104D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93

+:104D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83

+:104D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73

+:104D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63

+:104D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53

+:104D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43

+:104D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33

+:104D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23

+:104DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13

+:104DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03

+:104DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3

+:104DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3

+:104DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3

+:104DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3

+:104E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2

+:104E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2

+:104E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92

+:104E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82

+:104E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72

+:104E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62

+:104E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52

+:104E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42

+:104E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32

+:104E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22

+:104EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12

+:104EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02

+:104EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2

+:104ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2

+:104EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2

+:104EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2

+:104F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1

+:104F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1

+:104F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91

+:104F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81

+:104F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71

+:104F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61

+:104F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51

+:104F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41

+:104F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31

+:104F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21

+:104FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11

+:104FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01

+:104FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1

+:104FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1

+:104FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1

+:104FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1

+:10500000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0

+:10501000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0

+:10502000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

+:10503000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80

+:10504000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70

+:10505000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60

+:10506000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50

+:10507000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

+:10508000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30

+:10509000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20

+:1050A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10

+:1050B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00

+:1050C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

+:1050D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0

+:1050E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0

+:1050F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0

+:10510000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF

+:10511000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F

+:10512000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F

+:10513000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F

+:10514000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F

+:10515000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F

+:10516000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F

+:10517000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F

+:10518000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F

+:10519000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F

+:1051A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F

+:1051B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+:1051C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF

+:1051D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF

+:1051E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF

+:1051F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF

+:10520000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE

+:10521000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E

+:10522000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E

+:10523000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E

+:10524000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E

+:10525000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E

+:10526000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E

+:10527000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E

+:10528000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E

+:10529000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E

+:1052A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E

+:1052B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE

+:1052C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE

+:1052D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE

+:1052E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE

+:1052F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE

+:10530000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD

+:10531000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D

+:10532000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D

+:10533000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D

+:10534000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D

+:10535000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D

+:10536000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D

+:10537000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D

+:10538000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D

+:10539000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D

+:1053A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D

+:1053B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD

+:1053C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED

+:1053D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD

+:1053E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD

+:1053F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD

+:10540000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC

+:10541000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C

+:10542000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C

+:10543000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C

+:10544000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C

+:10545000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C

+:10546000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C

+:10547000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C

+:10548000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C

+:10549000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C

+:1054A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C

+:1054B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC

+:1054C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC

+:1054D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC

+:1054E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC

+:1054F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC

+:10550000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB

+:10551000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B

+:10552000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B

+:10553000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B

+:10554000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B

+:10555000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B

+:10556000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B

+:10557000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B

+:10558000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B

+:10559000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B

+:1055A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B

+:1055B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB

+:1055C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB

+:1055D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB

+:1055E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB

+:1055F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB

+:10560000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA

+:10561000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A

+:10562000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A

+:10563000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A

+:10564000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A

+:10565000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A

+:10566000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A

+:10567000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A

+:10568000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A

+:10569000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A

+:1056A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A

+:1056B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA

+:1056C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA

+:1056D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA

+:1056E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA

+:1056F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA

+:10570000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9

+:10571000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99

+:10572000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89

+:10573000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79

+:10574000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69

+:10575000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59

+:10576000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49

+:10577000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39

+:10578000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29

+:10579000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19

+:1057A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09

+:1057B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9

+:1057C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9

+:1057D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9

+:1057E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9

+:1057F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9

+:10580000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8

+:10581000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98

+:10582000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88

+:10583000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78

+:10584000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68

+:10585000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58

+:10586000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48

+:10587000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38

+:10588000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28

+:10589000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18

+:1058A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08

+:1058B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8

+:1058C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8

+:1058D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8

+:1058E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8

+:1058F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8

+:10590000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7

+:10591000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97

+:10592000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87

+:10593000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77

+:10594000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67

+:10595000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57

+:10596000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47

+:10597000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37

+:10598000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27

+:10599000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17

+:1059A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07

+:1059B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7

+:1059C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7

+:1059D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7

+:1059E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7

+:1059F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7

+:105A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6

+:105A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96

+:105A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86

+:105A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76

+:105A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66

+:105A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56

+:105A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46

+:105A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36

+:105A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26

+:105A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16

+:105AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06

+:105AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6

+:105AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6

+:105AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6

+:105AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6

+:105AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6

+:105B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5

+:105B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95

+:105B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85

+:105B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75

+:105B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65

+:105B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55

+:105B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45

+:105B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35

+:105B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25

+:105B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15

+:105BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05

+:105BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5

+:105BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5

+:105BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5

+:105BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5

+:105BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5

+:105C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4

+:105C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94

+:105C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84

+:105C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74

+:105C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64

+:105C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54

+:105C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44

+:105C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34

+:105C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24

+:105C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14

+:105CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04

+:105CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4

+:105CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4

+:105CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4

+:105CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4

+:105CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4

+:105D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3

+:105D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93

+:105D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83

+:105D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73

+:105D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63

+:105D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53

+:105D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43

+:105D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33

+:105D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23

+:105D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13

+:105DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03

+:105DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3

+:105DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3

+:105DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3

+:105DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3

+:105DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3

+:105E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2

+:105E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92

+:105E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82

+:105E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72

+:105E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62

+:105E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52

+:105E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42

+:105E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32

+:105E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22

+:105E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12

+:105EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02

+:105EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2

+:105EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2

+:105ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2

+:105EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2

+:105EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2

+:105F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1

+:105F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91

+:105F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81

+:105F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71

+:105F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61

+:105F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51

+:105F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41

+:105F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31

+:105F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21

+:105F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11

+:105FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01

+:105FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1

+:105FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1

+:105FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1

+:105FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1

+:105FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1

+:10600000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0

+:10601000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

+:10602000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80

+:10603000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70

+:10604000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60

+:10605000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50

+:10606000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

+:10607000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30

+:10608000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20

+:10609000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10

+:1060A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00

+:1060B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

+:1060C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0

+:1060D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0

+:1060E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0

+:1060F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0

+:10610000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F

+:10611000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F

+:10612000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F

+:10613000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F

+:10614000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F

+:10615000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F

+:10616000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F

+:10617000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F

+:10618000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F

+:10619000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F

+:1061A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+:1061B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF

+:1061C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF

+:1061D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF

+:1061E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF

+:1061F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF

+:10620000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E

+:10621000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E

+:10622000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E

+:10623000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E

+:10624000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E

+:10625000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E

+:10626000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E

+:10627000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E

+:10628000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E

+:10629000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E

+:1062A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE

+:1062B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE

+:1062C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE

+:1062D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE

+:1062E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE

+:1062F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE

+:10630000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D

+:10631000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D

+:10632000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D

+:10633000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D

+:10634000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D

+:10635000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D

+:10636000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D

+:10637000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D

+:10638000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D

+:10639000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D

+:1063A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD

+:1063B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED

+:1063C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD

+:1063D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD

+:1063E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD

+:1063F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD

+:10640000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C

+:10641000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C

+:10642000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C

+:10643000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C

+:10644000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C

+:10645000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C

+:10646000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C

+:10647000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C

+:10648000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C

+:10649000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C

+:1064A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC

+:1064B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC

+:1064C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC

+:1064D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC

+:1064E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC

+:1064F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC

+:10650000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B

+:10651000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B

+:10652000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B

+:10653000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B

+:10654000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B

+:10655000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B

+:10656000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B

+:10657000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B

+:10658000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B

+:10659000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B

+:1065A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB

+:1065B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB

+:1065C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB

+:1065D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB

+:1065E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB

+:1065F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB

+:10660000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A

+:10661000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A

+:10662000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A

+:10663000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A

+:10664000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A

+:10665000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A

+:10666000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A

+:10667000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A

+:10668000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A

+:10669000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A

+:1066A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA

+:1066B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA

+:1066C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA

+:1066D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA

+:1066E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA

+:1066F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA

+:10670000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99

+:10671000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89

+:10672000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79

+:10673000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69

+:10674000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59

+:10675000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49

+:10676000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39

+:10677000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29

+:10678000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19

+:10679000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09

+:1067A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9

+:1067B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9

+:1067C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9

+:1067D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9

+:1067E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9

+:1067F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9

+:10680000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98

+:10681000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88

+:10682000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78

+:10683000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68

+:10684000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58

+:10685000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48

+:10686000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38

+:10687000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28

+:10688000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18

+:10689000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08

+:1068A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8

+:1068B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8

+:1068C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8

+:1068D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8

+:1068E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8

+:1068F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8

+:10690000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97

+:10691000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87

+:10692000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77

+:10693000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67

+:10694000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57

+:10695000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47

+:10696000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37

+:10697000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27

+:10698000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17

+:10699000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07

+:1069A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7

+:1069B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7

+:1069C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7

+:1069D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7

+:1069E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7

+:1069F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7

+:106A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96

+:106A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86

+:106A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76

+:106A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66

+:106A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56

+:106A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46

+:106A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36

+:106A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26

+:106A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16

+:106A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06

+:106AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6

+:106AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6

+:106AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6

+:106AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6

+:106AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6

+:106AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6

+:106B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95

+:106B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85

+:106B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75

+:106B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65

+:106B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55

+:106B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45

+:106B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35

+:106B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25

+:106B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15

+:106B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05

+:106BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5

+:106BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5

+:106BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5

+:106BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5

+:106BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5

+:106BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5

+:106C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94

+:106C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84

+:106C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74

+:106C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64

+:106C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54

+:106C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44

+:106C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34

+:106C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24

+:106C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14

+:106C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04

+:106CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4

+:106CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4

+:106CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4

+:106CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4

+:106CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4

+:106CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4

+:106D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93

+:106D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83

+:106D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73

+:106D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63

+:106D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53

+:106D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43

+:106D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33

+:106D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23

+:106D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13

+:106D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03

+:106DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3

+:106DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3

+:106DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3

+:106DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3

+:106DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3

+:106DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3

+:106E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92

+:106E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82

+:106E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72

+:106E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62

+:106E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52

+:106E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42

+:106E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32

+:106E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22

+:106E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12

+:106E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02

+:106EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2

+:106EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2

+:106EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2

+:106ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2

+:106EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2

+:106EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2

+:106F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91

+:106F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81

+:106F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71

+:106F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61

+:106F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51

+:106F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41

+:106F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31

+:106F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21

+:106F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11

+:106F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01

+:106FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1

+:106FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1

+:106FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1

+:106FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1

+:106FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1

+:106FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1

+:10700000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

+:10701000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80

+:10702000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70

+:10703000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60

+:10704000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50

+:10705000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

+:10706000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30

+:10707000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20

+:10708000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10

+:10709000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00

+:1070A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

+:1070B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0

+:1070C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0

+:1070D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0

+:1070E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0

+:1070F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0

+:10710000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F

+:10711000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F

+:10712000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F

+:10713000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F

+:10714000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F

+:10715000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F

+:10716000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F

+:10717000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F

+:10718000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F

+:10719000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+:1071A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF

+:1071B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF

+:1071C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF

+:1071D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF

+:1071E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF

+:1071F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F

+:10720000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E

+:10721000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E

+:10722000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E

+:10723000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E

+:10724000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E

+:10725000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E

+:10726000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E

+:10727000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E

+:10728000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E

+:10729000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE

+:1072A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE

+:1072B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE

+:1072C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE

+:1072D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE

+:1072E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE

+:1072F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E

+:10730000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D

+:10731000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D

+:10732000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D

+:10733000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D

+:10734000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D

+:10735000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D

+:10736000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D

+:10737000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D

+:10738000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D

+:10739000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD

+:1073A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED

+:1073B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD

+:1073C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD

+:1073D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD

+:1073E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD

+:1073F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D

+:10740000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C

+:10741000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C

+:10742000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C

+:10743000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C

+:10744000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C

+:10745000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C

+:10746000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C

+:10747000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C

+:10748000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C

+:10749000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC

+:1074A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC

+:1074B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC

+:1074C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC

+:1074D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC

+:1074E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC

+:1074F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C

+:10750000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B

+:10751000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B

+:10752000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B

+:10753000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B

+:10754000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B

+:10755000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B

+:10756000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B

+:10757000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B

+:10758000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B

+:10759000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB

+:1075A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB

+:1075B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB

+:1075C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB

+:1075D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB

+:1075E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB

+:1075F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B

+:10760000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A

+:10761000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A

+:10762000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A

+:10763000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A

+:10764000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A

+:10765000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A

+:10766000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A

+:10767000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A

+:10768000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A

+:10769000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA

+:1076A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA

+:1076B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA

+:1076C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA

+:1076D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA

+:1076E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA

+:1076F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A

+:10770000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89

+:10771000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79

+:10772000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69

+:10773000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59

+:10774000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49

+:10775000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39

+:10776000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29

+:10777000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19

+:10778000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09

+:10779000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9

+:1077A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9

+:1077B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9

+:1077C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9

+:1077D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9

+:1077E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9

+:1077F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99

+:10780000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88

+:10781000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78

+:10782000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68

+:10783000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58

+:10784000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48

+:10785000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38

+:10786000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28

+:10787000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18

+:10788000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08

+:10789000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8

+:1078A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8

+:1078B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8

+:1078C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8

+:1078D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8

+:1078E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8

+:1078F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98

+:10790000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87

+:10791000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77

+:10792000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67

+:10793000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57

+:10794000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47

+:10795000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37

+:10796000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27

+:10797000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17

+:10798000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07

+:10799000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7

+:1079A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7

+:1079B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7

+:1079C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7

+:1079D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7

+:1079E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7

+:1079F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97

+:107A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86

+:107A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76

+:107A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66

+:107A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56

+:107A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46

+:107A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36

+:107A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26

+:107A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16

+:107A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06

+:107A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6

+:107AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6

+:107AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6

+:107AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6

+:107AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6

+:107AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6

+:107AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96

+:107B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85

+:107B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75

+:107B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65

+:107B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55

+:107B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45

+:107B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35

+:107B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25

+:107B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15

+:107B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05

+:107B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5

+:107BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5

+:107BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5

+:107BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5

+:107BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5

+:107BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5

+:107BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95

+:107C0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF84

+:107C1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF74

+:107C2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64

+:107C3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF54

+:107C4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF44

+:107C5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34

+:107C6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24

+:107C7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14

+:107C8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF04

+:107C9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4

+:107CA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4

+:107CB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD4

+:107CC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC4

+:107CD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB4

+:107CE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA4

+:107CF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF94

+:107D0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83

+:107D1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF73

+:107D2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF63

+:107D3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53

+:107D4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43

+:107D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33

+:107D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23

+:107D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13

+:107D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03

+:107D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3

+:107DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3

+:107DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3

+:107DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3

+:107DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3

+:107DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3

+:107DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93

+:107E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82

+:107E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72

+:107E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62

+:107E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52

+:107E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42

+:107E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32

+:107E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22

+:107E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12

+:107E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02

+:107E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2

+:107EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2

+:107EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2

+:107EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2

+:107ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2

+:107EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2

+:107EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92

+:107F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81

+:107F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71

+:107F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61

+:107F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51

+:107F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41

+:107F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31

+:107F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21

+:107F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11

+:107F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01

+:107F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1

+:107FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1

+:107FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1

+:107FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1

+:107FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1

+:107FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1

+:107FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91

+:10800000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80

+:10801000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70

+:10802000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60

+:10803000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50

+:10804000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

+:10805000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30

+:10806000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20

+:10807000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF10

+:10808000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00

+:10809000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

+:1080A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0

+:1080B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0

+:1080C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0

+:1080D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0

+:1080E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0

+:1080F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

+:10810000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F

+:10811000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F

+:10812000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F

+:10813000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4F

+:10814000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F

+:10815000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F

+:10816000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F

+:10817000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F

+:10818000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+:10819000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF

+:1081A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF

+:1081B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF

+:1081C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF

+:1081D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF

+:1081E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F

+:1081F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F

+:10820000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E

+:10821000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6E

+:10822000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E

+:10823000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E

+:10824000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E

+:10825000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E

+:10826000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E

+:10827000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E

+:10828000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE

+:10829000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEE

+:1082A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE

+:1082B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCE

+:1082C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE

+:1082D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAE

+:1082E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E

+:1082F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E

+:10830000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D

+:10831000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D

+:10832000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D

+:10833000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D

+:10834000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D

+:10835000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D

+:10836000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D

+:10837000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D

+:10838000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD

+:10839000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED

+:1083A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD

+:1083B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD

+:1083C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD

+:1083D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAD

+:1083E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D

+:1083F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8D

+:10840000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C

+:10841000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C

+:10842000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C

+:10843000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C

+:10844000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C

+:10845000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2C

+:10846000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C

+:10847000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0C

+:10848000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC

+:10849000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC

+:1084A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC

+:1084B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCC

+:1084C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC

+:1084D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAC

+:1084E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C

+:1084F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C

+:10850000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B

+:10851000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B

+:10852000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B

+:10853000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B

+:10854000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B

+:10855000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B

+:10856000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B

+:10857000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B

+:10858000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB

+:10859000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEB

+:1085A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB

+:1085B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCB

+:1085C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB

+:1085D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB

+:1085E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B

+:1085F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B

+:10860000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A

+:10861000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A

+:10862000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A

+:10863000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4A

+:10864000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A

+:10865000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A

+:10866000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A

+:10867000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A

+:10868000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA

+:10869000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEA

+:1086A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA

+:1086B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCA

+:1086C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA

+:1086D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAA

+:1086E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A

+:1086F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8A

+:10870000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79

+:10871000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF69

+:10872000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59

+:10873000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49

+:10874000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39

+:10875000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29

+:10876000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19

+:10877000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09

+:10878000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9

+:10879000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9

+:1087A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9

+:1087B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9

+:1087C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9

+:1087D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA9

+:1087E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99

+:1087F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF89

+:10880000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78

+:10881000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68

+:10882000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF58

+:10883000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF48

+:10884000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38

+:10885000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28

+:10886000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF18

+:10887000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF08

+:10888000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8

+:10889000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE8

+:1088A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD8

+:1088B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC8

+:1088C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8

+:1088D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA8

+:1088E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98

+:1088F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF88

+:10890000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF77

+:10891000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF67

+:10892000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF57

+:10893000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF47

+:10894000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF37

+:10895000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27

+:10896000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17

+:10897000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF07

+:10898000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7

+:10899000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7

+:1089A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD7

+:1089B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7

+:1089C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB7

+:1089D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA7

+:1089E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF97

+:1089F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF87

+:108A0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF76

+:108A1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF66

+:108A2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56

+:108A3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF46

+:108A4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF36

+:108A5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF26

+:108A6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF16

+:108A7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF06

+:108A8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6

+:108A9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE6

+:108AA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6

+:108AB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC6

+:108AC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6

+:108AD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6

+:108AE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96

+:108AF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF86

+:108B0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF75

+:108B1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF65

+:108B2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55

+:108B3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45

+:108B4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF35

+:108B5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF25

+:108B6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15

+:108B7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05

+:108B8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5

+:108B9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5

+:108BA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5

+:108BB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC5

+:108BC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB5

+:108BD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA5

+:108BE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF95

+:108BF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF85

+:108C000056623253030000004801000038000000A3

+:108C100010000000480000000001000000FC0000FF

+:108C200003000200419A32EEE5DBBCC148D6431393

+:108C30008799B21B5DA65DFF4861646F6B656E200E

+:108C400052656C65617365005A8C1F81980D34887C

+:108C5000C10CD306F1C37F0D77F951E77D9127BA97

+:108C60003A2276B815218B22624FEAC7A93DF56FEB

+:108C70006989039A7D14870F54EFAE6B3DCFD8BE40

+:108C80008C008537CAEE60889C9B9FEE6A42F7EAAB

+:108C900016DEA294053E744062938BD202D098BE39

+:108CA00070D639FED0732C587CA7E7411029355A6D

+:108CB000938B81CDD7A18992CC59D012FB22CB3096

+:108CC00018E05A3F5670B2BF070CC9ED8764D1ABAC

+:108CD000467F409640BE0F6BAA79D126FBB2C23BBD

+:108CE000F2BAFA8268DDB53813965503996C47A23B

+:108CF000354ED6B12EED696D2062FA70C2FCE35498

+:108D000059562D1F12279484ECFEBE149F464D85A4

+:108D1000855AB02938AC29DE05096A40A637B3FC6C

+:108D2000B93ECB2BC7D8255B109A2224D87C444966

+:108D30001D138D6A5D9AB629EFE928388E2E9FF6AD

+:108D4000CE88D691A6A93BF4FFFFFFFFFFFFFFFFF0

+:108D5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23

+:108D6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13

+:108D7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03

+:108D8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3

+:108D9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3

+:108DA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3

+:108DB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC3

+:108DC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB3

+:108DD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3

+:108DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF93

+:108DF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF83

+:108E0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF72

+:108E1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62

+:108E2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52

+:108E3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF42

+:108E4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF32

+:108E5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22

+:108E6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF12

+:108E7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02

+:108E8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2

+:108E9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2

+:108EA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2

+:108EB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC2

+:108EC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2

+:108ED000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2

+:108EE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF92

+:108EF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF82

+:108F0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF71

+:108F1000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61

+:108F2000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51

+:108F3000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41

+:108F4000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF31

+:108F5000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF21

+:108F6000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11

+:108F7000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01

+:108F8000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1

+:108F9000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE1

+:108FA000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD1

+:108FB000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC1

+:108FC000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB1

+:108FD000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA1

+:108FE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF91

+:108FF000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF81

+:00000001FF

diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/raw/init_151104_mp_signed.dat b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/raw/init_151104_mp_signed.dat
new file mode 100644
index 0000000..1088a40
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/raw/init_151104_mp_signed.dat
Binary files differ
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/colors.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/colors.xml
new file mode 100644
index 0000000..f8e9b79
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/colors.xml
@@ -0,0 +1,20 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/strings.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/strings.xml
new file mode 100644
index 0000000..8595996
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/strings.xml
@@ -0,0 +1,49 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Service name (for user) -->
+    <string name="app_name">
+        <xliff:g id="product_name">Pixel C</xliff:g> Keyboard Firmware Updater
+    </string>
+
+    <!-- Static information of the target keyboard. (for service use) -->
+    <string name="target_keyboard_name" translatable="false">Pixel C Keyboard</string>
+    <string name="target_manufacturer" translatable="false">Google</string>
+    <string name="target_battery_level" translatable="false">20</string>
+    <string name="target_firmware_version" translatable="false">0.151104</string>
+
+    <!-- File names of the firmware images in raw resource (for service use)-->
+    <string name="target_firmware_init_file_name" translatable="false">init_151104_mp_signed</string>
+    <string name="target_firmware_image_file_name" translatable="false">image_151104_mp_signed</string>
+
+    <!-- Update notification texts (for users) -->
+    <string name="notification_title">Keyboard update available</string>
+    <string name="notification_text">
+        <xliff:g id="product_name">Pixel C</xliff:g> keyboard update is ready
+    </string>
+    <string name="notification_later">LATER</string>
+    <string name="notification_install">INSTALL</string>
+
+    <!-- UpdateConfirmationActivity messages (for users) -->
+    <string name="confirmation_update_info">The keyboard will not work while it is updating.\nThis will take about 3 minutes.\n\nPlease do not disconnect the keyboard before the update completes.\n</string>
+    <string name="confirmation_install">INSTALL</string>
+
+    <!-- Update preference names (for service use)-->
+    <string name="preference_notification_flag" translatable="false">com.android.dragonkeyboardfirmwareupdater.preference_notification_flag</string>
+    <string name="preference_current_version" translatable="false">com.android.dragonkeyboardfirmwareupdater.preference_current_version</string>
+
+</resources>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/styles.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/styles.xml
new file mode 100644
index 0000000..6a4f2e3
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/styles.xml
@@ -0,0 +1,26 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="android:Theme.Material">
+        <!-- Customize your theme here. -->
+        <item name="android:colorPrimary">@color/colorPrimary</item>
+        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="android:colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/BootCompletedBroadcastReceiver.java b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/BootCompletedBroadcastReceiver.java
new file mode 100644
index 0000000..2b73330
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/BootCompletedBroadcastReceiver.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dragonkeyboardfirmwareupdater;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class BootCompletedBroadcastReceiver extends BroadcastReceiver {
+    private final static String TAG = BootCompletedBroadcastReceiver.class.getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+            // Start service after boot.
+            Log.d(TAG, "Start Pixel C Keyboard Firmware Updater service");
+            context.startService(new Intent(context, KeyboardFirmwareUpdateService.class));
+        }
+    }
+}
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/DfuService.java b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/DfuService.java
new file mode 100644
index 0000000..ab44ed0
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/DfuService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dragonkeyboardfirmwareupdater;
+
+import android.app.Activity;
+
+import no.nordicsemi.android.dfu.DfuBaseService;
+
+public class DfuService extends DfuBaseService {
+    @Override
+    protected Class<? extends Activity> getNotificationTarget() {
+        return NotificationActivity.class;
+    }
+}
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/GattAttributeUUID.java b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/GattAttributeUUID.java
new file mode 100644
index 0000000..2186d15
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/GattAttributeUUID.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dragonkeyboardfirmwareupdater;
+
+import java.util.UUID;
+
+public class GattAttributeUUID {
+    // Battery service
+    public final static UUID UUID_BATTERY_SERVICE =
+            UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb");
+    public final static UUID UUID_BATTERY_LEVEL_CHARACTERISTIC =
+            UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb");
+
+    // Device information service
+    public final static UUID UUID_DEVICE_INFORMATION_SERVICE =
+            UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb");
+    public final static UUID UUID_DEVICE_INFORMATION_MANUFACTURER_CHARACTERISTIC =
+            UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb");
+    public final static UUID UUID_DEVICE_INFORMATION_FIRMWARE_VERSION_CHARACTERISTIC =
+            UUID.fromString("00002a26-0000-1000-8000-00805f9b34fb");
+
+    // DFU control point service
+    public final static UUID UUID_DFU_SERVICE =
+            UUID.fromString("00001530-1212-efde-1523-785feabcd123");
+    public final static UUID UUID_DFU_CONTROL_POINT_CHARACTERISTIC =
+            UUID.fromString("00001531-1212-efde-1523-785feabcd123");
+    public final static UUID UUID_DFU_CONTROL_POINT_DESCRIPTOR =
+            UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
+}
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/KeyboardFirmwareUpdateService.java b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/KeyboardFirmwareUpdateService.java
new file mode 100644
index 0000000..248681b
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/KeyboardFirmwareUpdateService.java
@@ -0,0 +1,1114 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dragonkeyboardfirmwareupdater;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanResult;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+
+import java.util.List;
+import java.util.UUID;
+
+import no.nordicsemi.android.dfu.DfuProgressListener;
+import no.nordicsemi.android.dfu.DfuServiceListenerHelper;
+
+
+public class KeyboardFirmwareUpdateService extends Service {
+    private static final String TAG = KeyboardFirmwareUpdateService.class.getSimpleName();
+
+    /* Actions for update status changes. */
+    public static final String ACTION_KEYBOARD_UPDATE_CONFIRMED =
+            "com.android.dragonkeyboardfirmwareupdater.action.KEYBOARD_UPDATE_CONFIRMED";
+    public static final String ACTION_KEYBOARD_UPDATE_POSTPONED =
+            "com.android.dragonkeyboardfirmwareupdater.action.KEYBOARD_UPDATE_POSTPONED";
+    public static final String ACTION_KEYBOARD_UPDATE_STARTED =
+            "com.android.dragonkeyboardfirmwareupdater.action.KEYBOARD_UPDATE_STARTED";
+    public static final String ACTION_KEYBOARD_UPDATE_PROGRESS_CHANGED =
+            "com.android.dragonkeyboardfirmwareupdater.action.KEYBOARD_UPDATE_PROGRESS_CHANGED";
+    public static final String ACTION_KEYBOARD_UPDATE_ABORTED =
+            "com.android.dragonkeyboardfirmwareupdater.action.KEYBOARD_UPDATE_ABORTED";
+    public static final String ACTION_KEYBOARD_UPDATE_COMPLETED =
+            "com.android.dragonkeyboardfirmwareupdater.action.KEYBOARD_UPDATE_COMPLETED";
+
+    /* Actions for update notification flag. */
+    public static final String ACTION_KEYBOARD_UPDATE_NOTIFICATION_OFF =
+            "com.android.dragonkeyboardfirmwareupdater.action.KEYBOARD_UPDATE_NOTIFICATION_OFF";
+
+    /* Extra information for UpdaterConfirmationActivity. */
+    public static final String EXTRA_KEYBOARD_NAME =
+            "com.android.dragonkeyboardfirmwareupdater.EXTRA_KEYBOARD_NAME";
+    public static final String EXTRA_KEYBOARD_ADDRESS =
+            "com.android.dragonkeyboardfirmwareupdater.EXTRA_KEYBOARD_ADDRESS";
+    public static final String EXTRA_KEYBOARD_FIRMWARE_VERSION =
+            "com.android.dragonkeyboardfirmwareupdater.EXTRA_KEYBOARD_FIRMWARE_VERSION";
+
+    /**
+     * Bluetooth connectivity. The Bluetooth LE connection maintained in this service is for
+     * retrieving keyboard information, such as device manufacture and so on, and switching the
+     * keyboard to Device Update Mode (DFU). Once the DFU service is started, the connection
+     * maintained here and its corresponding variable should be cleaned up.
+     */
+    // Bluetooth Gatt connection state of the keyboard.
+    private static final int GATT_STATE_DISCONNECTED = 0;
+    private static final int GATT_STATE_CONNECTING = 1;
+    private static final int GATT_STATE_CONNECTED = 2;
+    private static final int GATT_STATE_DISCOVERING_SERVICES = 3;
+    private static final int GATT_STATE_DISCONNECTING = 4;
+    // Bluetooth system services.
+    private static final long SCAN_PERIOD = 7000;  // 7 seconds
+    private final IBinder mBinder = new LocalBinder();
+    private int mGattConnectionState = GATT_STATE_DISCONNECTED;
+    private int mGattOperationStatus = -1;
+    private BluetoothManager mBluetoothManager;
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothLeScanner mBluetoothLeScanner;
+    // Bluetooth Gatt connection bond with the keyboard.
+    private BluetoothGatt mBluetoothGattClient;
+    private String mKeyboardName;
+    private String mKeyboardAddress;
+    private String mKeyboardAddressInAppMode;
+    private String mKeyboardFirmwareVersion;
+    // Bluetooth Gatt services provided by the keyboard.
+    private BluetoothGattService mBatteryService;
+    private BluetoothGattService mDeviceInfoService;
+    private BluetoothGattService mDfuService;
+    private BluetoothGattCharacteristic mDfuChar;
+    // Bluetooth LE scan retry flag.
+    private boolean mLeScanRetried = false;
+    private int mDfuStatus = DFU_STATE_NOT_STARTED;
+
+    /* Handler for posting delayed tasks. */
+    private Handler mHandler;
+
+    /* Wake lock for DFU. */
+    private PowerManager.WakeLock mWakeLock;
+
+    /* Update notification. */
+    private static final int UPDATE_NOTIFICATION_ID = 1248;
+    private Notification mUpdateNotification;
+
+    /**
+     * Keeps track of the status of DFU process.
+     * DFU_STATE_NOT_STARTED: DFU not started
+     * DFU_STATE_OBTAINING_INFO: DFU not started, obtaining manufacturer, firmware version and battery level
+     * DFU_STATE_INFO_READY: DFU not started, manufacturer, firmware version and battery level obtained
+     * DFU_STATE_SWITCHING_TO_DFU_MODE: DFU not started, waiting for the keyboard to reboot into DFU mode
+     * DFU_STATE_MODE_SWITCHED: DFU not started, switched to DFU mode
+     * DFU_STATE_UPDATING: DFU started, pushing the new firmware to the keyboard
+     * DFU_STATE_UPDATE_COMPLETE: DFU finished correctly
+     * DFU_STATE_INFO_NOT_SUITABLE: DFU not started, the keyboard is not suitable for update
+     * DFU_STATE_OBTAIN_INFO_ERROR: DFU not started, error(s) occurred during obtaining information
+     * DFU_STATE_SWITCH_TO_DFU_MODE_ERROR: DFU not started, failed to switch to DFU mode
+     * DFU_STATE_UPDATE_ABORTED: DFU started but aborted by either users or errors during update
+     */
+    private static final int DFU_STATE_NOT_STARTED = 5;
+    private static final int DFU_STATE_OBTAINING_INFO = 6;
+    private static final int DFU_STATE_INFO_READY = 7;
+    private static final int DFU_STATE_SWITCHING_TO_DFU_MODE = 8;
+    private static final int DFU_STATE_MODE_SWITCHED = 9;
+    private static final int DFU_STATE_UPDATING = 10;
+    private static final int DFU_STATE_UPDATE_COMPLETE = 11;
+    private static final int DFU_STATE_INFO_NOT_SUITABLE = 12;
+    private static final int DFU_STATE_OBTAIN_INFO_ERROR = 13;
+    private static final int DFU_STATE_SWITCH_TO_DFU_MODE_ERROR = 14;
+    private static final int DFU_STATE_UPDATE_ABORTED = 15;
+
+    /* Handles Bluetooth LE scan results. Bluetooth LE scan occurs after DFU preparation is ready. */
+    private ScanCallback mBluetoothLeScanCallback = new ScanCallback() {
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            BluetoothDevice device = result.getDevice();
+            if (device == null || device.getName() == null) return;
+
+            // Find the keyboard in DFU mode and start DFU process. The name of keyboard in DFU mode
+            // is composed of the last three groups (G3:G4:G5) of its application mode address
+            // (G0:G1:G2:G3:G4:G5).
+            if (mKeyboardAddressInAppMode.endsWith(device.getName().toUpperCase()) &&
+                    mDfuStatus != DFU_STATE_UPDATING) {
+                Log.d(TAG, "onScanResult: Found target keyboard in DFU mode");
+
+                scanLeDevice(false);
+
+                // Start pushing new firmware to the keyboard.
+                startDfuService(device.getName(), device.getAddress());
+            }
+        }
+
+        @Override
+        public void onScanFailed(int errorCode) {
+            Log.w(TAG, "onScanFailed: Error Code: " + errorCode);
+            if (!mLeScanRetried) {
+                // Retry the scan once.
+                mLeScanRetried = true;
+                scanLeDevice(true);
+            }
+        }
+    };
+
+    /**
+     * Handles Bluetooth Gatt client callback. Read/write operations should finish in a certain
+     * order after DFU preparation starts.
+     */
+    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
+        private boolean retryFlag = true;
+
+        @Override
+        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+            assert gatt == mBluetoothGattClient;
+
+            switch (newState) {
+                case BluetoothProfile.STATE_CONNECTED:
+                    if (!checkOperationStatus(status)) {
+                        Log.w(TAG, "BluetoothGattCallback: Transferring to new state: " + newState +
+                                " failed with code: " + status);
+                        return;
+                    }
+
+                    changeGattState(GATT_STATE_CONNECTED);
+                    Log.i(TAG, "BluetoothGattCallback: Connected to Bluetooth Gatt server on " +
+                            getKeyboardString());
+                    // Start to discover services right after connection.
+                    mBluetoothGattClient.discoverServices();
+                    changeGattState(GATT_STATE_DISCOVERING_SERVICES);
+                    Log.d(TAG, "BluetoothGattCallback: Start to discover services on " +
+                            getKeyboardString());
+                    break;
+
+                case BluetoothProfile.STATE_DISCONNECTED:
+                    Log.i(TAG, "BluetoothGattCallback: Disconnected from Bluetooth Gatt server on "
+                            + getKeyboardString() + ", status: " + status);
+                    if (mGattConnectionState != GATT_STATE_DISCONNECTED) {
+                        changeGattState(GATT_STATE_DISCONNECTED);
+                    }
+                    cleanUpGattConnection();
+                    break;
+            }
+        }
+
+        @Override
+        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+            assert gatt == mBluetoothGattClient;
+
+            if (!checkOperationStatus(status)) {
+                changeDfuStatus(DFU_STATE_NOT_STARTED);
+                return;
+            }
+
+            if (!getGattServices()) return;
+
+            changeGattState(GATT_STATE_CONNECTED);
+            readBatteryLevel();
+        }
+
+        @Override
+        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+            assert gatt == mBluetoothGattClient;
+
+            if (!checkOperationStatus(status)) {
+                changeDfuStatus(DFU_STATE_OBTAIN_INFO_ERROR);
+                return;
+            }
+
+            UUID uuid = characteristic.getUuid();
+            if (GattAttributeUUID.UUID_BATTERY_LEVEL_CHARACTERISTIC.equals(uuid)) {
+
+                int batterLevel = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
+                if (batterLevel < Integer.parseInt(getString(R.string.target_battery_level))) {
+                    Log.w(TAG, "onCharacteristicRead BATTERY_LEVEL_CHARACTERISTIC: " +getKeyboardString()
+                            + " battery level(" + batterLevel + "%) is too low:");
+                    changeDfuStatus(DFU_STATE_INFO_NOT_SUITABLE);
+
+                    // TODO(mcchou): show charging notification if the battery level is too low.
+
+                    return;
+                }
+
+                readDeviceManufacturer();
+            } else if (GattAttributeUUID.UUID_DEVICE_INFORMATION_MANUFACTURER_CHARACTERISTIC.equals(uuid)) {
+
+                String manufacturer = new String(characteristic.getValue());
+                if (!manufacturer.equals(getString(R.string.target_manufacturer))) {
+                    Log.d(TAG, "onCharacteristicRead DEVICE_INFORMATION_MANUFACTURER_CHARACTERISTIC: Invalid manufacturer: "
+                            + manufacturer);
+                    changeDfuStatus(DFU_STATE_INFO_NOT_SUITABLE);
+                    return;
+                }
+
+                readDeviceFirmwareVersion();
+            } else if (GattAttributeUUID.UUID_DEVICE_INFORMATION_FIRMWARE_VERSION_CHARACTERISTIC.equals(uuid)) {
+
+                mKeyboardFirmwareVersion = new String(characteristic.getValue());
+                Log.d(TAG, "onCharacteristicRead DEVICE_INFORMATION_FIRMWARE_VERSION_CHARACTERISTIC: current: "
+                        + mKeyboardFirmwareVersion + " new: " + getString(R.string.target_firmware_version));
+
+                // TODO(mcchou): update target_fimware_version
+                if (Float.parseFloat(mKeyboardFirmwareVersion) >= Float.parseFloat(getString(R.string.target_firmware_version))) {
+                    Log.d(TAG, "onCharacteristicRead DEVICE_INFORMATION_FIRMWARE_VERSION_CHARACTERISTIC: " +
+                            getKeyboardString() + " firmware(" + mKeyboardFirmwareVersion +
+                            ") is up to date");
+                    changeDfuStatus(DFU_STATE_INFO_NOT_SUITABLE);
+                    return;
+                }
+
+                changeDfuStatus(DFU_STATE_INFO_READY);
+            }
+        }
+
+        @Override
+        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+            assert gatt == mBluetoothGattClient;
+
+            if (GattAttributeUUID.UUID_DFU_CONTROL_POINT_CHARACTERISTIC.equals(characteristic.getUuid())) {
+                Log.d(TAG, "onCharacteristicWrite DFU_CONTROL_POINT_CHARACTERISTIC: status: " + status);
+                changeDfuStatus(DFU_STATE_MODE_SWITCHED);
+            }
+        }
+
+        @Override
+        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+            assert gatt == mBluetoothGattClient;
+
+            if (GattAttributeUUID.UUID_DFU_CONTROL_POINT_DESCRIPTOR.equals(descriptor.getUuid())) {
+                Log.d(TAG, "onDescriptorWrite: DFU_CONTROL_POINT_DESCRIPTOR, status: " + status);
+                enableDfuMode();
+            }
+        }
+    };
+
+    /* Handles DfuService callback. DFU service starts after the keyboard is found in LE scan.*/
+    private DfuProgressListener mDfuProgressListener = new DfuProgressListener() {
+        private void cancelDfuServiceNotification() {
+            // Wait a bit before cancelling notification.
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    // If this activity is still open and upload process was completed, cancel the notification.
+                    final NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+                    manager.cancel(DfuService.NOTIFICATION_ID);
+                }
+            }, 200);
+        }
+
+        @Override
+        public void onDeviceConnecting(String deviceAddress) {
+        }
+
+        @Override
+        public void onDeviceConnected(String deviceAddress) {
+        }
+
+        @Override
+        public void onDfuProcessStarting(String deviceAddress) {
+        }
+
+        @Override
+        public void onDfuProcessStarted(String deviceAddress) {
+        }
+
+        @Override
+        public void onEnablingDfuMode(String deviceAddress) {
+        }
+
+        @Override
+        public void onProgressChanged(
+                String deviceAddress, int percent, float speed, float avgSpeed, int currentPart, int partsTotal) {
+            if ((percent % 5) == 0) {
+                Log.i(TAG, "DfuProgressListener: onProgressChanged: part" + currentPart + "/" +
+                        partsTotal + ", " + percent + "%");
+            }
+        }
+
+        @Override
+        public void onFirmwareValidating(String deviceAddress) {
+        }
+
+        @Override
+        public void onDeviceDisconnecting(String deviceAddress) {
+        }
+
+        @Override
+        public void onDeviceDisconnected(String deviceAddress) {
+        }
+
+        @Override
+        public void onDfuCompleted(String deviceAddress) {
+            Log.d(TAG, "DfuProgressListener: onDfuCompleted");
+            cancelDfuServiceNotification();
+            changeDfuStatus(DFU_STATE_UPDATE_COMPLETE);
+            mWakeLock.release();
+        }
+
+        @Override
+        public void onDfuAborted(String deviceAddress) {
+            Log.e(TAG, "DfuProgressListener: onDfuAborted");
+            cancelDfuServiceNotification();
+            changeDfuStatus(DFU_STATE_UPDATE_ABORTED);
+            mWakeLock.release();
+        }
+
+        @Override
+        public void onError(String deviceAddress, int error, int errorType, String message) {
+            Log.e(TAG, "DfuProgressListener: onError: " + message);
+            cancelDfuServiceNotification();
+            changeDfuStatus(DFU_STATE_UPDATE_ABORTED);
+            mWakeLock.release();
+        }
+    };
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            onHandleIntent(context, intent);
+        }
+    };
+
+    /* Dynamically creates intent filter for BroadcastReceiver. */
+    private static IntentFilter makeIntentFilter() {
+        IntentFilter intentFilter = new IntentFilter();
+
+        intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
+        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        intentFilter.addAction(ACTION_KEYBOARD_UPDATE_CONFIRMED);
+        intentFilter.addAction(ACTION_KEYBOARD_UPDATE_POSTPONED);
+
+        return intentFilter;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.d(TAG, "onCreate: " + getString(R.string.app_name));
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG, "onStartCommand: " + getString(R.string.app_name));
+
+        enableBluetoothConnectivity();
+        DfuServiceListenerHelper.registerProgressListener(this, mDfuProgressListener);
+        registerReceiver(mBroadcastReceiver, makeIntentFilter());
+        mHandler = new Handler();
+
+        // TODO(mcchou): Return proper flag.
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(TAG, "onDestroy" + getString(R.string.app_name));
+
+        disableBluetoothConnectivity();
+        DfuServiceListenerHelper.unregisterProgressListener(this, mDfuProgressListener);
+        unregisterReceiver(mBroadcastReceiver);
+    }
+
+    /**
+     * Handles intents ACTION_CONNECTION_STATE_CHANGED, ACTION_STATE_CHANGED,
+     * ACTION_BOND_STATE_CHANGED, ACTION_KEYBOARD_UPDATE_CONFIRMED.
+     * <p/>
+     * [ACTION_STATE_CHANGED]
+     * This action is used to keep track of ON/OFF state change on the system Bluetooth adapter.
+     * The
+     * purpose is to synchronize the local Bluetooth connectivity with system Bluetooth state.
+     * <p/>
+     * [ACTION_CONNECTION_STATE_CHANGED]
+     * This action is used to keep track of the connection change on the target device. The purpose
+     * is to synchronize the connection cycles of the local GATT connection and the system
+     * Bluetooth
+     * connection.
+     * <p/>
+     * [ACTION_BOND_STATE_CHANGED]
+     * This action is used to keep track of the bond state change on the target device. The purpose
+     * is to the connection cycles of the local GATT connection and the system Bluetooth
+     * connection.
+     * <p/>
+     * [ACTION_KEYBOARD_UPDATE_CONFIRMED]
+     * This action is used to receive the update confirmation from the user. The purpose is to
+     * trigger DFU process.
+     */
+    private void onHandleIntent(Context context, Intent intent) {
+        final String action = intent.getAction();
+        Log.d(TAG, "onHandleIntent: Received action: " + action);
+
+        if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+
+            if (!isBluetoothEnabled()) {
+                Log.w(TAG, "onHandleIntent: Bluetooth connectivity not enabled");
+                return;
+            }
+
+            // Match the connected device with the default keyboard name.
+            Bundle extras = intent.getExtras();
+            if (extras == null) return;
+            final BluetoothDevice device = extras.getParcelable(BluetoothDevice.EXTRA_DEVICE);
+            final int deviceConnectionState = extras.getInt(BluetoothAdapter.EXTRA_CONNECTION_STATE);
+
+            Log.d(TAG, "onHandleIntent: " + device.getName() + " [" + device.getAddress() +
+                    "] change to state: " + deviceConnectionState);
+
+            // Match the name of the target keyboard.
+            if (!isTargetKeyboard(device)) return;
+
+            if (deviceConnectionState == BluetoothAdapter.STATE_CONNECTED) {
+                // Prevent the second keyboard from using the service.
+                if (isUpdateServiceInUse()) return;
+
+                obtainKeyboardInfo(device.getName(), device.getAddress());
+
+                if (mDfuStatus != DFU_STATE_INFO_READY) {
+                    Log.w(TAG, "onHandleIntent: DFU preparation failed");
+                    changeDfuStatus(DFU_STATE_OBTAIN_INFO_ERROR);
+                    return;
+                }
+
+                showNotification();
+            } else if (deviceConnectionState == BluetoothAdapter.STATE_DISCONNECTING) {
+                handleGattDisconnection();
+            }
+
+        } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+            final int adapterState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+            if (adapterState == BluetoothAdapter.STATE_ON) {
+                if (!isBluetoothEnabled()) enableBluetoothConnectivity();
+            } else if (adapterState == BluetoothAdapter.STATE_TURNING_OFF) {
+                // Terminate update process and disable Bluetooth connectivity.
+                disableBluetoothConnectivity();
+                // Since BluetoothAdapter has been disabled, the callback of disconnection would not
+                // be called. Therefore a separate clean-up of GATT connection is need.
+                cleanUpGattConnection();
+            }
+
+        } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
+            Bundle extras = intent.getExtras();
+            if (extras == null) return;
+            final BluetoothDevice device = extras.getParcelable(BluetoothDevice.EXTRA_DEVICE);
+            final int deviceBondState = extras.getInt(BluetoothDevice.EXTRA_BOND_STATE);
+
+            Log.d(TAG, "onHandleIntent: state change on device " + device.getName() + " [" +
+                    device.getAddress() + "], bond state: " + deviceBondState);
+
+            if (!isTargetKeyboard(device)) return;
+
+            if (deviceBondState == BluetoothDevice.BOND_NONE) {
+                handleGattDisconnection();
+            }
+
+        } else if (ACTION_KEYBOARD_UPDATE_CONFIRMED.equals(action)) {
+            dismissNotification();
+
+            if (mDfuStatus != DFU_STATE_INFO_READY || mDfuStatus == DFU_STATE_UPDATING) {
+                Log.w(TAG, "onHandleIntent: DFP preparation not ready or DFU is in progress. ");
+                changeDfuStatus(DFU_STATE_UPDATE_ABORTED);
+                return;
+            }
+
+            String keyboardName = intent.getStringExtra(EXTRA_KEYBOARD_NAME);
+            String keyboardAddress = intent.getStringExtra(EXTRA_KEYBOARD_ADDRESS);
+            if (!mKeyboardName.equals(keyboardName) || !mKeyboardAddress.equals(keyboardAddress)) {
+                Log.w(TAG, "onHandleIntent: No DFU service associated with " + keyboardName + " [" +
+                        keyboardAddress + "]");
+                return;
+            }
+
+            Log.d(TAG, "onHandleIntent: Start update process on " + keyboardName + " [" +
+                    keyboardAddress + "]");
+            changeDfuStatus(DFU_STATE_SWITCHING_TO_DFU_MODE);
+
+        } else if (ACTION_KEYBOARD_UPDATE_POSTPONED.equals(action)) {
+            dismissNotification();
+            // TODO(mcchou): Update the preference when the Settings keyboard entry is available.
+        }
+    }
+
+    /* Checks if Bluetooth connectivity is enabled. */
+    private boolean isBluetoothEnabled() {
+        return (mBluetoothManager != null && mBluetoothAdapter != null && mBluetoothLeScanner != null);
+    }
+
+    /* Checks if there is already a keyboard associated with the update service. */
+    private boolean isUpdateServiceInUse() {
+        return (mKeyboardName != null && mKeyboardAddress != null);
+    }
+
+    /* Returns a string including the keyboard name and address. */
+    private String getKeyboardString() {
+        return mKeyboardName + " [" + mKeyboardAddress + "]";
+    }
+
+    private boolean isTargetKeyboard(BluetoothDevice device) {
+        return (device != null && getString(R.string.target_keyboard_name).equals(device.getName()));
+    }
+
+    /* Retrieves Bluetooth manager, adapter and scanner. */
+    private boolean enableBluetoothConnectivity() {
+        Log.d(TAG, "EnableBluetoothConnectivity");
+        if (mBluetoothManager == null) {
+            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+            if (mBluetoothManager == null) {
+                Log.w(TAG, "EnableBluetoothConnectivity: Failed to obtain BluetoothManager");
+                return false;
+            }
+        }
+
+        mBluetoothAdapter = mBluetoothManager.getAdapter();
+        if (mBluetoothAdapter == null) {
+            Log.w(TAG, "EnableBluetoothConnectivity: Failed to obtain BluetoothAdapter");
+            return false;
+        }
+
+        mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
+        if (mBluetoothLeScanner == null) {
+            Log.w(TAG, "EnableBluetoothConnectivity: Failed to obtain BluetoothLeScanner");
+            return false;
+        }
+
+        // The first auto-connection after boot might be missed due to starting time of the updater service.
+        List<BluetoothDevice> connectedDevices = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
+        for (BluetoothDevice device : connectedDevices) {
+            if (isTargetKeyboard(device) && !isUpdateServiceInUse()) {
+                Log.d(TAG, "enableBluetoothConnectivity: Found keyboard " + device.getName() + " [" +
+                        device.getAddress() + "] connected");
+                obtainKeyboardInfo(device.getName(), device.getAddress());
+                break;
+            }
+        }
+
+        return true;
+    }
+
+    /* Disables Bluetooth connectivity if exists. */
+    private void disableBluetoothConnectivity() {
+        Log.d(TAG, "disableBluetoothConnectivity");
+        handleGattDisconnection();
+        try {
+            Thread.sleep(3000);  // 3 seconds
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        mBluetoothManager = null;
+        mBluetoothAdapter = null;
+        mBroadcastReceiver = null;
+    }
+
+    /* Show the update notification. */
+    private void showNotification() {
+        Log.d(TAG, "showNotification: " + getKeyboardString());
+
+        // Intent for triggering the update confirmation page.
+        Intent updateConfirmation = new Intent(this, UpdateConfirmationActivity.class);
+        updateConfirmation.putExtra(EXTRA_KEYBOARD_NAME, mKeyboardName);
+        updateConfirmation.putExtra(EXTRA_KEYBOARD_ADDRESS, mKeyboardAddress);
+        updateConfirmation.putExtra(EXTRA_KEYBOARD_FIRMWARE_VERSION, mKeyboardFirmwareVersion);
+        updateConfirmation.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        // Intent for postponing update.
+        Intent postponeUpdate = new Intent(ACTION_KEYBOARD_UPDATE_POSTPONED);
+
+        // Wrap intents into pending intents for notification use.
+        PendingIntent laterIntent = PendingIntent.getBroadcast(
+                this, 0, postponeUpdate, PendingIntent.FLAG_UPDATE_CURRENT);
+        PendingIntent installIntent = PendingIntent.getActivity(
+                this, 0, updateConfirmation, PendingIntent.FLAG_CANCEL_CURRENT);
+
+        // Create a notification object with two buttons (actions)
+        mUpdateNotification = new Notification.Builder(this)
+                .setContentTitle(getString(R.string.notification_title))
+                .setContentText(getString(R.string.notification_text))
+                .setSmallIcon(R.drawable.ic_keyboard)
+                .addAction(new Notification.Action.Builder(
+                        R.drawable.ic_later, getString(R.string.notification_later),
+                        laterIntent).build())
+                .addAction(new Notification.Action.Builder(
+                        R.drawable.ic_install, getString(R.string.notification_install),
+                        installIntent).build())
+                .setAutoCancel(true)
+                .setOnlyAlertOnce(true)
+                .build();
+
+        // Show the notification via notification manager
+        NotificationManager notificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        notificationManager.notify(UPDATE_NOTIFICATION_ID, mUpdateNotification);
+    }
+
+    /* Dismisses the udpate notification. */
+    private void dismissNotification() {
+        if (mUpdateNotification == null) return;
+        Log.d(TAG, "dismissNotification");
+        NotificationManager notificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        notificationManager.cancel(UPDATE_NOTIFICATION_ID);
+        mUpdateNotification = null;
+    }
+
+    /* Connects to the GATT server hosted on the given Bluetooth LE device. */
+    private boolean connectToKeyboard() {
+        if (!isBluetoothEnabled() || !isUpdateServiceInUse()) {
+            Log.w(TAG, "connectToKeyboard: Bluetooth connectivity not enabled or associated keyboard not found.");
+            return false;
+        }
+
+        final BluetoothDevice keyboard = mBluetoothAdapter.getRemoteDevice(mKeyboardAddress);
+        if (keyboard == null) {
+            Log.w(TAG, "connectToKeyboard: " + getKeyboardString() + " not found. Unable to connect.");
+            return false;
+        }
+
+        Log.d(TAG, "connectToKeyboard: Trying to create a new connection to " + getKeyboardString());
+        mBluetoothGattClient = keyboard.connectGatt(this, false, mGattCallback);
+        changeGattState(GATT_STATE_CONNECTING);
+        mGattOperationStatus = BluetoothGatt.GATT_SUCCESS;
+
+        return true;
+    }
+
+    /* Disconnects from the GATT server hosted on the given Bluetooth LE device. */
+    private void disconnectFromKeyboard() {
+        if (mGattConnectionState == GATT_STATE_DISCONNECTED) return;
+        if (!isUpdateServiceInUse() || !isBluetoothEnabled() || mDfuStatus == DFU_STATE_NOT_STARTED) {
+            Log.i(TAG, "disconnectFromKeyboard: Bluetooth connectivity not enabled");
+            return;
+        }
+
+        Log.d(TAG, "disconnectFromKeyboard: " + getKeyboardString());
+
+        mBluetoothGattClient.disconnect();
+        changeGattState(GATT_STATE_DISCONNECTING);
+        mGattOperationStatus = BluetoothGatt.GATT_SUCCESS;
+    }
+
+    /**
+     * Cleans up Bluetooth GATT connection and the keyboard. This should be done before starting
+     * DFU process.
+     */
+    private void cleanUpGattConnection() {
+        Log.d(TAG, "cleanUpGattConnection");
+        mKeyboardName = null;
+        mKeyboardAddress = null;
+        mKeyboardFirmwareVersion = null;
+        mBluetoothGattClient = null;
+        mBatteryService = null;
+        mDeviceInfoService = null;
+        mDfuService = null;
+        mDfuChar = null;
+        mLeScanRetried = false;
+    }
+
+    /* Starts to collect the information of the keyboard. */
+    private void obtainKeyboardInfo(String keyboardName, String keyboardAddress) {
+        Log.d(TAG, "obtainKeyboardInfo: Obtain the information of " + keyboardName + " [" +
+                keyboardAddress + "]");
+
+        // Connect to the keyboard and start to obtain its information.
+        mKeyboardName = keyboardName;
+        mKeyboardAddress = keyboardAddress.toUpperCase();
+        mKeyboardAddressInAppMode = mKeyboardAddress;
+        Log.d(TAG, "obtainKeyboardInfo: Associate DFU service with " + getKeyboardString());
+
+        if (mGattConnectionState == GATT_STATE_CONNECTED) {
+            Log.i(TAG, "obtainKeyboardInfo: Reuse previous GATT connection");
+            readBatteryLevel();
+        } else if (mGattConnectionState == GATT_STATE_DISCONNECTED) {
+            changeDfuStatus(DFU_STATE_OBTAINING_INFO);
+        } else {
+            Log.w(TAG, "obtainKeyboardInfo: Failed to obtain keyboard information");
+        }
+
+        // Wait at most 10 seconds for the queries to GATT attributes to finish.
+        int waitTimes = 5;
+        while (mDfuStatus == DFU_STATE_OBTAINING_INFO && waitTimes > 0) {
+            try {
+                Thread.sleep(2000);  // 2 seconds
+                waitTimes--;
+                Log.d(TAG, "obtainKeyboardInfo: Wait for preparation completion");
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /* Starts/stops Bluetooth LE scan. */
+    private void scanLeDevice(final boolean enable) {
+        if (!isBluetoothEnabled()) {
+            Log.w(TAG, "scanLeDevice: Bluetooth connectivity not enabled");
+        }
+        if (enable) {
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    Log.d(TAG, "scanLeDevice: Stop scanning");
+                    mBluetoothLeScanner.stopScan(mBluetoothLeScanCallback);
+                }
+            }, SCAN_PERIOD);
+            Log.d(TAG, "scanLeDevice: Start scanning");
+            mBluetoothLeScanner.startScan(mBluetoothLeScanCallback);
+        } else {
+            Log.d(TAG, "scanLeDevice: Stop scanning");
+            mBluetoothLeScanner.stopScan(mBluetoothLeScanCallback);
+        }
+    }
+
+    /**
+     * Retrieves Battery Service, Device Information Service, Device Firmware Update(DFU) service
+     * and DFU Control Point Characteristic.
+     */
+    private boolean getGattServices() {
+        Log.d(TAG, "getDfuServiceAndChar");
+
+        if (mBluetoothGattClient == null) {
+            Log.w(TAG, "getDfuServiceAndChar: Bluetooth GATT connection not initiated");
+            return false;
+        }
+
+        mBatteryService = mBluetoothGattClient.getService(GattAttributeUUID.UUID_BATTERY_SERVICE);
+        if (mBatteryService == null) {
+            Log.e(TAG, "getBatteryService: Failed to get Battery Service");
+            return false;
+        }
+        mDeviceInfoService = mBluetoothGattClient.getService(GattAttributeUUID.UUID_DEVICE_INFORMATION_SERVICE);
+        if (mDeviceInfoService == null) {
+            Log.e(TAG, "getDeviceInfoService: Failed to get Device Information Service");
+            return false;
+        }
+
+        mDfuService = mBluetoothGattClient.getService(GattAttributeUUID.UUID_DFU_SERVICE);
+        if (mDfuService == null) {
+            Log.e(TAG, "getDfuServiceAndChar: Failed to get Device Firmware Update Service");
+            return false;
+        }
+
+        mDfuChar = mDfuService.getCharacteristic(GattAttributeUUID.UUID_DFU_CONTROL_POINT_CHARACTERISTIC);
+        if (mDfuChar == null) {
+            Log.e(TAG, "getDfuServiceAndChar: Failed to get DFU Control Point characteristic");
+            return false;
+        }
+
+        return true;
+    }
+
+    /* Retrieves battery level of the connected keyboard. */
+    private boolean readBatteryLevel() {
+        Log.d(TAG, "readBatteryLevel");
+
+        BluetoothGattCharacteristic batteryLevelChar = mBatteryService.getCharacteristic(
+                GattAttributeUUID.UUID_BATTERY_LEVEL_CHARACTERISTIC);
+        if (batteryLevelChar == null || !mBluetoothGattClient.readCharacteristic(batteryLevelChar)) {
+            Log.e(TAG, "readBatteryLevel: Failed to init batter level read operation");
+            return false;
+        }
+        return true;
+    }
+
+    /* Retrieves device manufacturer of the connected keyboard. */
+    private boolean readDeviceManufacturer() {
+        Log.d(TAG, "readDeviceManufacturer");
+
+        BluetoothGattCharacteristic deviceManufacturerChar = mDeviceInfoService.getCharacteristic(
+                GattAttributeUUID.UUID_DEVICE_INFORMATION_MANUFACTURER_CHARACTERISTIC);
+        if (deviceManufacturerChar == null ||
+                !mBluetoothGattClient.readCharacteristic(deviceManufacturerChar)) {
+            Log.e(TAG, "readDeviceInfo: Failed to init device manufacturer characteristic read operation");
+            return false;
+        }
+        return true;
+    }
+
+    /* Retrieves device firmware version of the connected keyboard. */
+    private boolean readDeviceFirmwareVersion() {
+        Log.d(TAG, "readDeviceFirmwareVersion");
+
+        BluetoothGattCharacteristic deviceFirmwareVersionChar = mDeviceInfoService.getCharacteristic(
+                GattAttributeUUID.UUID_DEVICE_INFORMATION_FIRMWARE_VERSION_CHARACTERISTIC);
+        if (deviceFirmwareVersionChar == null ||
+                !mBluetoothGattClient.readCharacteristic(deviceFirmwareVersionChar)) {
+            Log.e(TAG, "readDeviceInfo: Failed to get device firmware revision characteristic");
+            return false;
+        }
+        return true;
+    }
+
+    /* Enables device firmware update notification of the connected keyboard. */
+    private boolean enableDfuNotification() {
+        Log.d(TAG, "enableDfuNotification");
+
+        if (mDfuChar == null) {
+            Log.w(TAG, "enableDfuNotification: DFU control point characteristic not initiated");
+            return false;
+        }
+
+        BluetoothGattDescriptor dfuDesc = mDfuChar.getDescriptor(
+                GattAttributeUUID.UUID_DFU_CONTROL_POINT_DESCRIPTOR);
+        if (dfuDesc == null ||
+                !dfuDesc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) ||
+                !mBluetoothGattClient.writeDescriptor(dfuDesc)) {
+            Log.e(TAG, "enableDfuNotification: Failed to init DFU descriptor write operation");
+            return false;
+        }
+        return true;
+    }
+
+    /* Switches the connected keyboard to DFU mode. */
+    private boolean enableDfuMode() {
+        Log.d(TAG, "enableDfuMode");
+
+        if (mDfuChar == null) {
+            Log.w(TAG, "enableDfuMode: DFU control point characteristic not initiated");
+            return false;
+        }
+
+        // Opcode: 0x01 -> Start DFU mode.
+        //         0x04 -> DFU type: application
+        final byte dfuOpcodeWithTypeApplication[] = new byte[]{0x01, 0x04};
+        if (!mDfuChar.setValue(dfuOpcodeWithTypeApplication) ||
+                !mBluetoothGattClient.writeCharacteristic(mDfuChar)) {
+            Log.e(TAG, "enableDfuMode: Failed to init DFU mode switch");
+            return false;
+        }
+        return true;
+    }
+
+    /* Checks if a Bluetooth Gatt operation is finished correctly. */
+    private boolean checkOperationStatus(int status) {
+        mGattOperationStatus = status;
+        if (mGattOperationStatus != BluetoothGatt.GATT_SUCCESS) {
+            Log.d(TAG, "BluetoothGattCallback: GATT operation failure: " + mGattOperationStatus);
+            return false;
+        }
+        return true;
+    }
+
+    /* Starts DFU process. */
+    private void startDfuService(String keyboardName, String keyboardAddress) {
+        Log.d(TAG, "startDfuService");
+
+        changeDfuStatus(DFU_STATE_UPDATING);
+
+        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+        mWakeLock.acquire();
+
+        String packageName = getApplicationContext().getPackageName();
+        int initResourceId = getResources().getIdentifier(
+            getString(R.string.target_firmware_init_file_name), "raw", packageName);
+        int imageResourceId = getResources().getIdentifier(
+            getString(R.string.target_firmware_image_file_name), "raw", packageName);
+        boolean keepBond = true;
+
+        Log.d(TAG, "Name: " + keyboardName + "\n" +
+                "Address: " + keyboardAddress + "\n" +
+                "Init file: " + getString(R.string.target_firmware_init_file_name) + "\n" +
+                "Image file: " + getString(R.string.target_firmware_image_file_name) + "\n" +
+                "Image type: Application(" + DfuService.TYPE_APPLICATION + ")\n" +
+                "Keep bond: " + keepBond);
+
+        final Intent service = new Intent(this, DfuService.class);
+        service.putExtra(DfuService.EXTRA_DEVICE_NAME, keyboardName);
+        service.putExtra(DfuService.EXTRA_DEVICE_ADDRESS, keyboardAddress);
+        service.putExtra(DfuService.EXTRA_INIT_FILE_RES_ID, initResourceId);
+        service.putExtra(DfuService.EXTRA_FILE_RES_ID, imageResourceId);
+        service.putExtra(DfuService.EXTRA_FILE_TYPE, DfuService.TYPE_APPLICATION);
+        service.putExtra(DfuService.EXTRA_KEEP_BOND, true);
+
+        startService(service);
+    }
+
+    /* Aborts DFU service if it is in progress. */
+    public void abortDfu() {
+        if (mDfuStatus != DFU_STATE_UPDATING) return;
+        final Intent pauseAction = new Intent(DfuService.BROADCAST_ACTION);
+        pauseAction.putExtra(DfuService.EXTRA_ACTION, DfuService.ACTION_ABORT);
+        LocalBroadcastManager.getInstance(this).sendBroadcast(pauseAction);
+    }
+
+    /* State setter of GATT connection. */
+    private void changeGattState(int newStatus) {
+        mGattConnectionState = newStatus;
+        Log.i(TAG, "-- changeGattState: " + getGattStateString(mGattConnectionState));
+    }
+
+    /* Helper function for logging GATT state change. */
+    private String getGattStateString(int state) {
+        switch (state) {
+            case GATT_STATE_DISCONNECTED:
+                return "GATT_STATE_DISCONNECTED";
+            case GATT_STATE_CONNECTING:
+                return "GATT_STATE_CONNECTING";
+            case GATT_STATE_CONNECTED:
+                return "GATT_STATE_CONNECTED";
+            case GATT_STATE_DISCOVERING_SERVICES:
+                return "GATT_STATE_DISCOVERING_SERVICES";
+            case GATT_STATE_DISCONNECTING:
+                return "GATT_STATE_DISCONNECTING";
+            default:
+                return "Unknown state (" + state + ")";
+        }
+    }
+
+    /* State flow for the updater service. */
+    private void changeDfuStatus(int newStatus) {
+        int nextStatus = newStatus;
+        switch (newStatus) {
+            case DFU_STATE_NOT_STARTED:
+                break;
+            case DFU_STATE_OBTAINING_INFO:
+                connectToKeyboard();
+                break;
+            case DFU_STATE_INFO_READY:
+                // TODO(mcchou): Send info intent to Settings.
+                break;
+            case DFU_STATE_SWITCHING_TO_DFU_MODE:
+                // TODO(mcchou): Send update in progress to Settings.
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (mDfuStatus == DFU_STATE_SWITCHING_TO_DFU_MODE)
+                            changeDfuStatus(DFU_STATE_SWITCH_TO_DFU_MODE_ERROR);
+                    }
+                }, SCAN_PERIOD * 2);
+                enableDfuNotification();
+                break;
+            case DFU_STATE_MODE_SWITCHED:
+                scanLeDevice(true);
+                break;
+            case DFU_STATE_UPDATING:
+                // TODO(mcchou): Send progress intent to Settings.
+                break;
+            case DFU_STATE_UPDATE_COMPLETE:
+                // TODO(mcchou): Send update complete to Settings.
+                nextStatus = DFU_STATE_NOT_STARTED;
+                break;
+            case DFU_STATE_INFO_NOT_SUITABLE:
+                // TODO(mcchou): Send fail intent to Settings.
+                disconnectFromKeyboard();
+                nextStatus = DFU_STATE_NOT_STARTED;
+                break;
+            case DFU_STATE_OBTAIN_INFO_ERROR:
+                // TODO(mcchou): Send fail intent to Settings.
+                disconnectFromKeyboard();
+                nextStatus = DFU_STATE_NOT_STARTED;
+                break;
+            case DFU_STATE_SWITCH_TO_DFU_MODE_ERROR:
+                // TODO(mcchou): Send abort intent to Settings.
+                nextStatus = DFU_STATE_INFO_READY;
+                break;
+            case DFU_STATE_UPDATE_ABORTED:
+                // TODO(mcchou): Send abort intent to Settings.
+                nextStatus = DFU_STATE_NOT_STARTED;
+                break;
+            default:
+                break;
+        }
+        mDfuStatus = newStatus;
+        Log.d(TAG, "---- changeDfuStatus: " + getDfuStateString(mDfuStatus));
+        if (nextStatus != newStatus) changeDfuStatus(nextStatus);
+    }
+
+    /* Helper function for logging DFU state change. */
+    private String getDfuStateString(int state) {
+        switch (state) {
+            case DFU_STATE_NOT_STARTED:
+                return "DFU_STATE_NOT_STARTED";
+            case DFU_STATE_OBTAINING_INFO:
+                return "DFU_STATE_OBTAINING_INFO";
+            case DFU_STATE_INFO_READY:
+                return "DFU_STATE_INFO_READY";
+            case DFU_STATE_SWITCHING_TO_DFU_MODE:
+                return "DFU_STATE_SWITCHING_TO_DFU_MODE";
+            case DFU_STATE_MODE_SWITCHED:
+                return "DFU_STATE_MODE_SWITCHED";
+            case DFU_STATE_UPDATING:
+                return "DFU_STATE_UPDATING";
+            case DFU_STATE_UPDATE_COMPLETE:
+                return "DFU_STATE_UPDATE_COMPLETE";
+            case DFU_STATE_INFO_NOT_SUITABLE:
+                return "DFU_STATE_INFO_NOT_SUITABLE";
+            case DFU_STATE_OBTAIN_INFO_ERROR:
+                return "DFU_STATE_OBTAIN_INFO_ERROR";
+            case DFU_STATE_SWITCH_TO_DFU_MODE_ERROR:
+                return "DFU_STATE_SWITCH_TO_DFU_MODE_ERROR";
+            case DFU_STATE_UPDATE_ABORTED:
+                return "DFU_STATE_UPDATE_ABORTED";
+            default:
+                return "Unknown state (" + state + ")";
+        }
+    }
+
+    /* Handles GATT disconnection. */
+    private void handleGattDisconnection() {
+        Log.d(TAG, "handleGattDisconnection");
+        if (mGattConnectionState == GATT_STATE_DISCONNECTED) return;
+
+        // TODO(mcchou): add fall through comment
+        // Handle update process termination based on the current DFU state.
+        switch (mDfuStatus) {
+            case DFU_STATE_SWITCHING_TO_DFU_MODE:
+                scanLeDevice(false);
+            case DFU_STATE_OBTAINING_INFO:
+            case DFU_STATE_INFO_READY:
+            case DFU_STATE_INFO_NOT_SUITABLE:
+            case DFU_STATE_SWITCH_TO_DFU_MODE_ERROR:
+                disconnectFromKeyboard();
+            case DFU_STATE_NOT_STARTED:
+            case DFU_STATE_MODE_SWITCHED:
+            case DFU_STATE_UPDATE_COMPLETE:
+            case DFU_STATE_UPDATE_ABORTED:
+                break;
+            case DFU_STATE_UPDATING:
+                abortDfu();
+                dismissNotification();
+                break;
+            default:
+                break;
+        }
+        changeGattState(GATT_STATE_DISCONNECTED);
+    }
+
+    public class LocalBinder extends Binder {
+        KeyboardFirmwareUpdateService getService() {
+            return KeyboardFirmwareUpdateService.this;
+        }
+    }
+}
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/NotificationActivity.java b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/NotificationActivity.java
new file mode 100644
index 0000000..a1a39a4
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/NotificationActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dragonkeyboardfirmwareupdater;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class NotificationActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (isTaskRoot()) {
+            // TODO(mcchou): Add the root service.
+        }
+        finish();
+    }
+}
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/UpdateConfirmationActivity.java b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/UpdateConfirmationActivity.java
new file mode 100644
index 0000000..a8b6b1b
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/UpdateConfirmationActivity.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dragonkeyboardfirmwareupdater;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class UpdateConfirmationActivity extends Activity {
+    private final static String TAG = UpdateConfirmationActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.d(TAG, "onCreate");
+
+        setContentView(R.layout.activity_update_confirmation);
+
+        final Button installButton = (Button) findViewById(R.id.confirmation_install_button);
+        installButton.setVisibility(View.VISIBLE);
+        installButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                installButton.setEnabled(false);
+                installButton.setVisibility(View.INVISIBLE);
+
+                final Intent intent = getIntent();
+                final Intent updateIntent = new Intent(
+                        KeyboardFirmwareUpdateService.ACTION_KEYBOARD_UPDATE_CONFIRMED);
+                updateIntent.putExtra(
+                        KeyboardFirmwareUpdateService.EXTRA_KEYBOARD_NAME,
+                        intent.getStringExtra(KeyboardFirmwareUpdateService.EXTRA_KEYBOARD_NAME));
+                updateIntent.putExtra(
+                        KeyboardFirmwareUpdateService.EXTRA_KEYBOARD_ADDRESS,
+                        intent.getStringExtra(KeyboardFirmwareUpdateService.EXTRA_KEYBOARD_ADDRESS));
+                sendBroadcast(updateIntent);
+
+                // Close update confirmation page.
+                finish();
+            }
+        });
+    }
+}
diff --git a/device.mk b/device.mk
index 0048a5c..6233f12 100644
--- a/device.mk
+++ b/device.mk
@@ -56,7 +56,8 @@
     crash_dispatcher \
     crash_collector \
     CrashReportProvider \
-    fwtool
+    fwtool \
+    DragonKeyboardFirmwareUpdater
 
 ifeq ($(TARGET_BUILD_VARIANT),eng)
 PRODUCT_PACKAGES += \