From 3dbba8a8814c691c1f452036b58f985d381a214b Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Tue, 3 Feb 2015 15:16:05 +0100 Subject: [PATCH] nRF Logger removed from DfuLibrary. BSD license added to each file. --- dfu/dfu.iml | 3 +- dfu/libs/nrf-logger-v2.0.jar | Bin 29046 -> 0 bytes dfu/sources/nrf-logger-v2.0-source.jar | Bin 20458 -> 0 bytes .../android/dfu/DfuBaseService.java | 651 +++++++++--------- .../android/dfu/DfuSettingsConstants.java | 22 + .../android/dfu/HexInputStream.java | 30 +- .../android/dfu/ZipHexInputStream.java | 22 + .../DeviceDisconnectedException.java | 30 +- .../android/dfu/exception/DfuException.java | 30 +- .../exception/HexFileValidationException.java | 30 +- .../dfu/exception/RemoteDfuException.java | 30 +- .../exception/UnknownResponseException.java | 30 +- .../dfu/exception/UploadAbortedException.java | 30 +- .../nordicsemi/android/error/GattError.java | 28 +- 14 files changed, 553 insertions(+), 383 deletions(-) delete mode 100644 dfu/libs/nrf-logger-v2.0.jar delete mode 100644 dfu/sources/nrf-logger-v2.0-source.jar diff --git a/dfu/dfu.iml b/dfu/dfu.iml index 90a2c9a..b126957 100644 --- a/dfu/dfu.iml +++ b/dfu/dfu.iml @@ -1,5 +1,5 @@ - + @@ -84,7 +84,6 @@ - diff --git a/dfu/libs/nrf-logger-v2.0.jar b/dfu/libs/nrf-logger-v2.0.jar deleted file mode 100644 index 8d37a964baae3847c39b4bc0ef56de97b5ee9ae0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29046 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 ve^l>2 - * With the Soft Device 7.0.0+ it allows to upload a new Soft Device, new Bootloader and a new Application (marked here as DFU v2.0). For older soft devices only the Application update is supported. + * 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. *

- * To run the service to your application inherit it and overwrite the missing method. Remember to it to the AndroidManifest.xml file. Start the service with the following parameters: + * 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. + *

+ *

+ * Start the service with the following parameters: *

*

  * final Intent service = new Intent(this, YourDfuService.class);
- * service.putExtra(DfuService.EXTRA_DEVICE_ADDRESS, mSelectedDevice.getAddress());
- * service.putExtra(DfuService.EXTRA_DEVICE_NAME, mSelectedDevice.getName());
+ * service.putExtra(DfuService.EXTRA_DEVICE_ADDRESS, mSelectedDevice.getAddress()); // Target device address
+ * service.putExtra(DfuService.EXTRA_DEVICE_NAME, mSelectedDevice.getName()); // This name will be shown on the notification
  * service.putExtra(DfuService.EXTRA_FILE_MIME_TYPE, mFileType == DfuService.TYPE_AUTO ? YourDfuService.MIME_TYPE_ZIP : YourDfuService.MIME_TYPE_OCTET_STREAM);
  * service.putExtra(DfuService.EXTRA_FILE_TYPE, mFileType);
  * service.putExtra(DfuService.EXTRA_FILE_PATH, mFilePath);
@@ -79,17 +92,14 @@ import no.nordicsemi.android.log.Logger;
  * startService(service);
  * 
*

- * The {@link #EXTRA_FILE_MIME_TYPE} and {@link #EXTRA_FILE_TYPE} parameters are optional. If not provided the application upload from HEX/BIN file is assumed. The service API is compatible with - * previous versions. + * The {@link #EXTRA_FILE_MIME_TYPE} and {@link #EXTRA_FILE_TYPE} parameters are optional. If not provided the application upload from HEX/BIN file is assumed. + * The service API is compatible with previous versions. *

*

- * The service will show its progress in the notifications bar and will send local broadcasts to the application. + * The service will show its progress on the notification bar and will send local broadcasts to the application. *

*/ public abstract class DfuBaseService extends IntentService { - private static final String TAG = "DfuService"; - private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); - /** * The address of the device to update. */ @@ -100,45 +110,55 @@ public abstract class DfuBaseService extends IntentService { public static final String EXTRA_DEVICE_NAME = "no.nordicsemi.android.dfu.extra.EXTRA_DEVICE_NAME"; /** *

- * If the new firmware does not share the bond information with the old one the bond information is lost. Set this flag to true to make the service create new bond with the new - * application when the upload is done (and remove the old one). When set to false (default), the DFU service assumes that the LTK is shared between them. Currently it is not possible - * to remove the old bond and not 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. + * If the new firmware (application) does not share the bond information with the old one, the bond information is lost. Set this flag to true + * to make the service create new bond with the new application when the upload is done (and remove the old one). When set to false (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. *

*

- * In case of updating the soft device and bootloader the application is always removed so the bond information with it. + * In case of updating the soft device the application is always removed so the bond information with it. *

*

* Search for occurrences of EXTRA_RESTORE_BOND in this file to check the implementation and get more details. *

*/ public static final String EXTRA_RESTORE_BOND = "no.nordicsemi.android.dfu.extra.EXTRA_RESTORE_BOND"; - public static final String EXTRA_LOG_URI = "no.nordicsemi.android.dfu.extra.EXTRA_LOG_URI"; + /** + * 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. + */ 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"; /** - * The Init packet URI. This file is required if Extended Init Packet is required. 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. + * 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 path. This file is required if Extended Init Packet is required. 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. + * 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 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. + * 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 7.0 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: *
    *
  • {@link #TYPE_SOFT_DEVICE} - only Soft Device update
  • *
  • {@link #TYPE_BOOTLOADER} - only Bootloader update
  • *
  • {@link #TYPE_APPLICATION} - only application update
  • - *
  • {@link #TYPE_AUTO} -the file is a ZIP file that may contain more than one HEX. The ZIP file MAY contain only the following files: softdevice.hex, bootloader.hex, - * application.hex to determine the type based on its name. At lease one of them MUST be present.
  • + *
  • {@link #TYPE_AUTO} - the file is a ZIP file that may contain more than one HEX/BIN + DAT files. The ZIP file MAY contain only the following files: + * softdevice.hex/bin, bootloader.hex/bin, application.hex/bin to determine the type based on its name. At lease one of them MUST be present. + *
  • *
* If this parameter is not provided the type is assumed as follows: *
    @@ -198,7 +218,6 @@ public abstract class DfuBaseService extends IntentService { * @see #EXTRA_FILE_TYPE */ public static final int TYPE_AUTO = 0x00; - /** * Extra to send progress and error broadcast events. */ @@ -247,7 +266,6 @@ public abstract class DfuBaseService extends IntentService { * 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: *
      @@ -300,7 +318,6 @@ public abstract class DfuBaseService extends IntentService { * 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: *
        @@ -312,7 +329,7 @@ public abstract class DfuBaseService extends IntentService { /** * 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; // When user tries to connect to more than 6 devices on Nexus devices (Android 4.4.4) the 0x101 error is thrown. Mask changed 0x0100 -> 0x1000 to avoid collision. + 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; /** @@ -359,19 +376,50 @@ public abstract class DfuBaseService extends IntentService { * 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 log events are only broadcasted when there is no nRF Logger installed. The broadcast contains 2 extras: + * The log events are only broadcast when there is no nRF Logger installed. The broadcast contains 2 extras: *
          - *
        • {@link #EXTRA_LOG_LEVEL} - The log level: one of {@link LogContract.Log.Level#DEBUG}, {@link LogContract.Log.Level#VERBOSE}, {@link LogContract.Log.Level#INFO}, - * {@link LogContract.Log.Level#WARNING}, {@link LogContract.Log.Level#ERROR}
        • - *
        • {@link #EXTRA_LOG_MESSAGE}
        • + *
        • {@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}
        • + *
        • {@link #EXTRA_LOG_MESSAGE}
        • - The log message *
        */ 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"; + /** + * 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; + /* + * The nRF Logger API library has been excluded from 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 + */ + /** + * 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. */ @@ -380,18 +428,64 @@ public abstract class DfuBaseService extends IntentService { public static final int ACTION_PAUSE = 0; public static final int ACTION_RESUME = 1; public static final int ACTION_ABORT = 2; - + // Values of the status field in a packet returned from the DFU target device + 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; + // public static final int NOTIFICATION_ID = 283; // a random number - - private BluetoothAdapter mBluetoothAdapter; - private String mDeviceAddress; - private String mDeviceName; - private ILogSession mLogSession; + private static final String TAG = "DfuBaseService"; + // Values of the mConnectionState variable + 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; + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + private final static 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]; + private static final int OP_CODE_START_DFU_KEY = 0x01; // 1 + // Operation codes and packets + private static final byte[] OP_CODE_START_DFU = new byte[]{OP_CODE_START_DFU_KEY, 0x00}; + private static final int OP_CODE_INIT_DFU_PARAMS_KEY = 0x02; // 2 + 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 int OP_CODE_RECEIVE_FIRMWARE_IMAGE_KEY = 0x03; // 3 + private static final byte[] OP_CODE_RECEIVE_FIRMWARE_IMAGE = new byte[]{OP_CODE_RECEIVE_FIRMWARE_IMAGE_KEY}; + private static final int OP_CODE_VALIDATE_KEY = 0x04; // 4 + private static final byte[] OP_CODE_VALIDATE = new byte[]{OP_CODE_VALIDATE_KEY}; + private static final int OP_CODE_ACTIVATE_AND_RESET_KEY = 0x05; // 5 + private static final byte[] OP_CODE_ACTIVATE_AND_RESET = new byte[]{OP_CODE_ACTIVATE_AND_RESET_KEY}; + private static final int OP_CODE_RESET_KEY = 0x06; // 6 + private static final byte[] OP_CODE_RESET = new byte[]{OP_CODE_RESET_KEY}; + // 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 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}; + private static final int OP_CODE_RESPONSE_CODE_KEY = 0x10; // 16 + private static final int OP_CODE_PACKET_RECEIPT_NOTIF_KEY = 0x11; // 11 + // 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); + // + private static final int NOTIFICATIONS = 1; + private static final int INDICATIONS = 2; /** * Lock used in synchronization purposes */ private final Object mLock = new Object(); - + private BluetoothAdapter mBluetoothAdapter; + private String mDeviceAddress; + private String mDeviceName; /** * The number of the last error that has occurred or 0 if there was no error */ @@ -400,13 +494,25 @@ public abstract class DfuBaseService extends IntentService { * 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; + 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(); + } + } + }; /** * Flag set when we got confirmation from the device that notifications are enabled. */ @@ -415,14 +521,10 @@ public abstract class DfuBaseService extends IntentService { * Flag set when we got confirmation from the device that Service Changed indications are enabled. */ private boolean mServiceChangedIndicationsEnabled; - - private final static int MAX_PACKET_SIZE = 20; // the maximum number of bytes in one packet is 20. May be less. /** * 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; - - private final byte[] mBuffer = new byte[MAX_PACKET_SIZE]; private InputStream mInputStream; /** * Size of BIN content of all hex files that are going to be transmitted. @@ -453,92 +555,6 @@ public abstract class DfuBaseService extends IntentService { private int mFileType; private boolean mPaused; private boolean mAborted; - 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; - /** - *

        - * Flag set to true when the DFU target had send any notification with status other than {@link #DFU_STATUS_SUCCESS}. Setting it to true will abort sending firmware and - * stop logging notifications (read below for explanation). - *

        - *

        - * The onCharacteristicWrite(..) callback is written when Android puts the packet to the outgoing queue, not when it physically send the data. Therefore, in case of invalid state of the DFU - * target, Android will first put up to N* packets, one by one, while in fact the first will be 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. However, just after receiving the first one this service will try to send the reset command while still getting more - * 10-03-02 notifications. This flag will prevent from logging "Notification received..." more than once. - *

        - *

        - * 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. - *

        - *

        - * N* - Value of Packet Receipt Notification, 10 by default. - *

        - */ - private boolean mRemoteErrorOccurred; - - /** - * Latest data received from device using notification. - */ - private byte[] mReceivedData = null; - 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}; - - 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; - - 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); - - private static final int NOTIFICATIONS = 1; - private static final int INDICATIONS = 2; - - // Since the DFU Library version 7.0 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"; - private final BroadcastReceiver mDfuActionReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { @@ -568,27 +584,24 @@ public abstract class DfuBaseService extends IntentService { } } }; - - 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 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; private final BroadcastReceiver mBondStateBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { @@ -610,7 +623,30 @@ public abstract class DfuBaseService extends IntentService { } } }; - + /** + *

        + * Flag set to true when the DFU target had send any notification with status other than {@link #DFU_STATUS_SUCCESS}. Setting it to true will abort sending firmware and + * stop logging notifications (read below for explanation). + *

        + *

        + * The onCharacteristicWrite(..) callback is written when Android puts the packet to the outgoing queue, not when it physically send the data. Therefore, in case of invalid state of the DFU + * target, Android will first put up to N* packets, one by one, while in fact the first will be 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. However, just after receiving the first one this service will try to send the reset command while still getting more + * 10-03-02 notifications. This flag will prevent from logging "Notification received..." more than once. + *

        + *

        + * 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. + *

        + *

        + * N* - Value of Packet Receipt Notification, 10 by default. + *

        + */ + private boolean mRemoteErrorOccurred; + /** + * Latest data received from device using notification. + */ + private byte[] mReceivedData = null; private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { @@ -749,7 +785,7 @@ public abstract class DfuBaseService extends IntentService { if (mAborted || mError != 0 || mRemoteErrorOccurred || mResetRequestSent) { // notify waiting thread synchronized (mLock) { - sendLogBroadcast(Level.WARNING, "Upload terminated"); + sendLogBroadcast(LOG_LEVEL_WARNING, "Upload terminated"); mLock.notifyAll(); return; } @@ -769,16 +805,16 @@ public abstract class DfuBaseService extends IntentService { } } else if (!mImageSizeSent) { // we've got confirmation that the image size was sent - sendLogBroadcast(Level.INFO, "Data written to " + characteristic.getUuid() + ", value (0x): " + parse(characteristic)); + 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(Level.INFO, "Data written to " + characteristic.getUuid() + ", value (0x): " + parse(characteristic)); + 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 - sendLogBroadcast(Level.INFO, "Data written to " + characteristic.getUuid() + ", value (0x): " + parse(characteristic)); + sendLogBroadcast(LOG_LEVEL_INFO, "Data written to " + characteristic.getUuid() + ", value (0x): " + parse(characteristic)); mRequestCompleted = true; } } else { @@ -806,7 +842,7 @@ public abstract class DfuBaseService extends IntentService { /* * This method is called when the DFU Version characteristic has been read. */ - sendLogBroadcast(Level.INFO, "Read Response received from " + characteristic.getUuid() + ", value (0x): " + parse(characteristic)); + sendLogBroadcast(LOG_LEVEL_INFO, "Read Response received from " + characteristic.getUuid() + ", value (0x): " + parse(characteristic)); mReceivedData = characteristic.getValue(); mRequestCompleted = true; } else { @@ -836,7 +872,7 @@ public abstract class DfuBaseService extends IntentService { // 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(Level.WARNING, "Upload terminated"); + sendLogBroadcast(LOG_LEVEL_WARNING, "Upload terminated"); break; } @@ -866,7 +902,7 @@ public abstract class DfuBaseService extends IntentService { if (status != DFU_STATUS_SUCCESS) mRemoteErrorOccurred = true; - sendLogBroadcast(Level.INFO, "Notification received from " + characteristic.getUuid() + ", value (0x): " + parse(characteristic)); + sendLogBroadcast(LOG_LEVEL_INFO, "Notification received from " + characteristic.getUuid() + ", value (0x): " + parse(characteristic)); mReceivedData = characteristic.getValue(); break; } @@ -896,11 +932,21 @@ public abstract class DfuBaseService extends IntentService { return new String(out); } }; + /** + * Stores the last progress percent. Used to lower number of calls of {@link #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(); @@ -943,26 +989,24 @@ public abstract class DfuBaseService extends IntentService { final Uri fileUri = intent.getParcelableExtra(EXTRA_FILE_URI); final String initFilePath = intent.getStringExtra(EXTRA_INIT_FILE_PATH); final Uri initFileUri = intent.getParcelableExtra(EXTRA_INIT_FILE_URI); - final Uri logUri = intent.getParcelableExtra(EXTRA_LOG_URI); 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); // FIXME check if it's better - mLogSession = Logger.openSession(this, logUri); 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(Level.WARNING, "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(Level.WARNING, "Unable to determine file type"); + sendLogBroadcast(LOG_LEVEL_WARNING, "Unable to determine file type"); sendErrorBroadcast(ERROR_FILE_TYPE_UNSUPPORTED); return; } @@ -997,7 +1041,7 @@ public abstract class DfuBaseService extends IntentService { if (!packetReceiptNotificationEnabled) numberOfPackets = 0; mPacketsBeforeNotification = numberOfPackets; - // 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. + // 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; @@ -1009,7 +1053,7 @@ public abstract class DfuBaseService extends IntentService { mbrSize = DfuSettingsConstants.SETTINGS_DEFAULT_MBR_SIZE; } - sendLogBroadcast(Level.VERBOSE, "Starting DFU service"); + sendLogBroadcast(LOG_LEVEL_VERBOSE, "Starting DFU service"); /* * First the service is trying to read the firmware and init packet files. @@ -1020,7 +1064,7 @@ public abstract class DfuBaseService extends IntentService { try { // Prepare data to send, calculate stream size try { - sendLogBroadcast(Level.VERBOSE, "Opening file..."); + sendLogBroadcast(LOG_LEVEL_VERBOSE, "Opening file..."); if (fileUri != null) { is = openInputStream(fileUri, mimeType, mbrSize, fileType); } else { @@ -1054,7 +1098,7 @@ public abstract class DfuBaseService extends IntentService { initIs = new ByteArrayInputStream(zhis.getSystemInit()); } } - sendLogBroadcast(Level.INFO, "Image file opened (" + mImageSizeInBytes + " bytes in total)"); + 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); @@ -1073,27 +1117,27 @@ public abstract class DfuBaseService extends IntentService { * Now let's connect to the device. * All the methods below are synchronous. The mLock object is used to wait for asynchronous calls. */ - sendLogBroadcast(Level.VERBOSE, "Connecting to DFU target..."); + 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(Level.ERROR, "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_MASK; loge("An error occurred while connecting to the device:" + error); - sendLogBroadcast(Level.ERROR, String.format("Connection failed (0x%02X): %s", error, GattError.parse(error))); + sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Connection failed (0x%02X): %s", error, GattError.parse(error))); terminateConnection(gatt, mError); return; } if (mAborted) { logi("Upload aborted"); - sendLogBroadcast(Level.WARNING, "Upload aborted"); + sendLogBroadcast(LOG_LEVEL_WARNING, "Upload aborted"); terminateConnection(gatt, PROGRESS_ABORTED); return; } @@ -1102,7 +1146,7 @@ public abstract class DfuBaseService extends IntentService { 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(Level.WARNING, "Connected. DFU Service not found"); + sendLogBroadcast(LOG_LEVEL_WARNING, "Connected. DFU Service not found"); terminateConnection(gatt, ERROR_SERVICE_NOT_FOUND); return; } @@ -1110,29 +1154,28 @@ public abstract class DfuBaseService extends IntentService { final BluetoothGattCharacteristic packetCharacteristic = dfuService.getCharacteristic(DFU_PACKET_UUID); if (controlPointCharacteristic == null || packetCharacteristic == null) { loge("DFU characteristics not found in the DFU service"); - sendLogBroadcast(Level.WARNING, "Connected. DFU Characteristics not found"); + 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) - Device is connected to the application, not to the 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. + * 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) - Device is in OTA-DFU Bootloader. The Bootloader supports LTK sharing and required the Extended Init Packet. It supports * a SoftDevice, Bootloader or an Application update. SoftDevice and a Bootloader may be sent together. - * */ final BluetoothGattCharacteristic versionCharacteristic = dfuService.getCharacteristic(DFU_VERSION); // this may be null for older versions of the Bootloader - sendLogBroadcast(Level.INFO, "Connected. Services discovered"); + sendLogBroadcast(LOG_LEVEL_INFO, "Connected. Services discovered"); try { updateProgressNotification(PROGRESS_STARTING); @@ -1143,21 +1186,21 @@ public abstract class DfuBaseService extends IntentService { final int minor = (version & 0x0F); final int major = (version >> 8); logi("Version number read: " + major + "." + minor); - sendLogBroadcast(Level.APPLICATION, "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. + * we are in the bootloader or application by simply checking the number of characteristics. */ if (version == 1 || (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(Level.WARNING, "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. + // 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) { @@ -1166,41 +1209,41 @@ public abstract class DfuBaseService extends IntentService { final BluetoothGattCharacteristic serviceChangedCharacteristic = genericAttributeService.getCharacteristic(SERVICE_CHANGED_UUID); if (serviceChangedCharacteristic != null) { enableCCCD(gatt, serviceChangedCharacteristic, INDICATIONS); - sendLogBroadcast(Level.APPLICATION, "Service Changed indications enabled"); + sendLogBroadcast(LOG_LEVEL_APPLICATION, "Service Changed indications enabled"); hasServiceChanged = true; } } } - sendLogBroadcast(Level.VERBOSE, "Jumping to the DFU Bootloader..."); + sendLogBroadcast(LOG_LEVEL_VERBOSE, "Jumping to the DFU Bootloader..."); // Enable notifications enableCCCD(gatt, controlPointCharacteristic, NOTIFICATIONS); - sendLogBroadcast(Level.APPLICATION, "Notifications enabled"); + 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(Level.APPLICATION, "Jump to bootloader sent (Op Code = 1, Upload Mode = 4)"); + 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(Level.INFO, "Disconnected by the remote device"); + 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. + * 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. + * For bonded devices Android performs service re-discovery when SC indication is received. */ refreshDeviceCache(gatt, !hasServiceChanged); @@ -1216,7 +1259,7 @@ public abstract class DfuBaseService extends IntentService { // Enable notifications enableCCCD(gatt, controlPointCharacteristic, NOTIFICATIONS); - sendLogBroadcast(Level.APPLICATION, "Notifications enabled"); + sendLogBroadcast(LOG_LEVEL_APPLICATION, "Notifications enabled"); try { // Set up the temporary variable that will hold the responses @@ -1242,7 +1285,7 @@ public abstract class DfuBaseService extends IntentService { * If 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 DFU v.1 a response [10, 01, 03] will be send as a notification on DFU Control Point characteristic, where: + * If DFU target supports only the DFU v.1 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 @@ -1270,12 +1313,12 @@ public abstract class DfuBaseService extends IntentService { // 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(Level.APPLICATION, "DFU Start sent (Op Code = 1, Upload Mode = " + fileType + ")"); + 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(Level.APPLICATION, "Firmware image size sent (" + softDeviceImageSize + "b, " + bootloaderImageSize + "b, " + appImageSize + "b)"); + 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(); @@ -1283,15 +1326,15 @@ public abstract class DfuBaseService extends IntentService { /* * The response received from the DFU device contains: * +---------+--------+----------------------------------------------------+ - * | byte no | value | description | + * | 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 | + * | 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(Level.APPLICATION, "Response received (Op Code = " + response[1] + " Status = " + status + ")"); + 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) { @@ -1299,14 +1342,14 @@ public abstract class DfuBaseService extends IntentService { if (e.getErrorNumber() != DFU_STATUS_NOT_SUPPORTED) throw e; - // If user wants to send soft device and/or bootloader + application we may try to send the Soft Device/Bootloader files first, + // If user wants to send soft device and/or bootloader + application we may try to send the Soft Device/Bootloader files first, // and then reconnect and send the application 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(Level.WARNING, "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; @@ -1324,20 +1367,20 @@ public abstract class DfuBaseService extends IntentService { } // send Start DFU command to Control Point - sendLogBroadcast(Level.VERBOSE, "Sending only SD/BL"); + 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(Level.APPLICATION, "DFU Start sent (Op Code = 1, Upload Mode = " + fileType + ")"); + 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(Level.APPLICATION, "Firmware image size sent [" + softDeviceImageSize + "b, " + bootloaderImageSize + "b, " + appImageSize + "b]"); + 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(Level.APPLICATION, "Response received (Op Code = " + response[1] + " Status = " + status + ")"); + 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 @@ -1353,23 +1396,23 @@ public abstract class DfuBaseService extends IntentService { // The DFU target does not support DFU v.2 protocol logw("DFU target does not support DFU v.2"); - sendLogBroadcast(Level.WARNING, "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(Level.VERBOSE, "Switching to DFU v.1"); + 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(Level.APPLICATION, "DFU Start sent (Op Code = 1)"); + 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(Level.APPLICATION, "Firmware image size sent (" + imageSizeInBytes + " bytes)"); + 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(Level.APPLICATION, "Response received (Op Code = " + response[1] + ", Status = " + status + ")"); + 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 @@ -1396,21 +1439,21 @@ public abstract class DfuBaseService extends IntentService { * 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. * To calculate the CRC (CRC-CCTII-16 0xFFFF) the following application may be used: http://www.lammertbies.nl/comm/software/index.html -> CRC library. - * + * * The Init Packet is read from the [firmware].dat file as a binary file. This means: * 1. If the firmware is in HEX or BIN file, f.e. my_firmware.hex (or .bin), the init packet will be read from my_firmware.dat file. * 2. If the new firmware consists of more files (combined in the ZIP) or the ZIP file is used to store it, the ZIP must additionally contain the following files: - * + * * 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 - * (f.e. using: http://hex2bin.sourceforge.net/ application), put into one file where the soft device is placed as the first one and calculate the CRC for the - * whole big file. - * + * (f.e. using: http://hex2bin.sourceforge.net/ application), put into one file where the soft device is placed as the first one and calculate the CRC for the + * whole big 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. */ if (initIs != null) { - sendLogBroadcast(Level.APPLICATION, "Writing Initialize DFU Parameters..."); + 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); @@ -1427,12 +1470,12 @@ public abstract class DfuBaseService extends IntentService { } logi("Sending the Initialize DFU Parameters COMPLETE (Op Code = 2, Value = 1)"); writeOpCode(gatt, controlPointCharacteristic, OP_CODE_INIT_DFU_PARAMS_COMPLETE); - sendLogBroadcast(Level.APPLICATION, "Initialize DFU Parameters completed"); + 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(Level.APPLICATION, "Response received (Op Code = " + response[1] + ", Status = " + status + ")"); + 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 @@ -1444,20 +1487,20 @@ public abstract class DfuBaseService extends IntentService { 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(Level.APPLICATION, "Packet Receipt Notif Req (Op Code = 8) sent (Value = " + numberOfPacketsBeforeNotification + ")"); + 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(Level.APPLICATION, "Receive Firmware Image request sent"); + sendLogBroadcast(LOG_LEVEL_APPLICATION, "Receive Firmware Image request sent"); // This allow us to calculate upload time final long startTime = mLastProgressTime = mStartTime = SystemClock.elapsedRealtime(); updateProgressNotification(); try { - logi("Starting upload..."); - sendLogBroadcast(Level.APPLICATION, "Starting upload..."); + logi("Uploading firmware..."); + sendLogBroadcast(LOG_LEVEL_APPLICATION, "Uploading firmware..."); response = uploadFirmwareImage(gatt, packetCharacteristic, is); } catch (final DeviceDisconnectedException e) { loge("Disconnected while sending data"); @@ -1469,23 +1512,23 @@ public abstract class DfuBaseService extends IntentService { // 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(Level.APPLICATION, "Response received (Op Code = " + response[1] + ", Status = " + status + ")"); + 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(Level.APPLICATION, "Upload completed in " + (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(Level.APPLICATION, "Validate request sent"); + 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(Level.APPLICATION, "Response received (Op Code = " + response[1] + ", Status = " + status + ")"); + 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); @@ -1496,11 +1539,11 @@ public abstract class DfuBaseService extends IntentService { // Send Activate and Reset signal. logi("Sending Activate and Reset request (Op Code = 5)"); writeOpCode(gatt, controlPointCharacteristic, OP_CODE_ACTIVATE_AND_RESET); - sendLogBroadcast(Level.APPLICATION, "Activate and Reset request sent"); + sendLogBroadcast(LOG_LEVEL_APPLICATION, "Activate and Reset request sent"); // The device will reset so we don't have to send Disconnect signal. waitUntilDisconnected(); - sendLogBroadcast(Level.INFO, "Disconnected by the remote device"); + sendLogBroadcast(LOG_LEVEL_INFO, "Disconnected by the remote device"); refreshDeviceCache(gatt, true); // The new application may have lost bonding information (if there was bonding). Force refresh it just to be sure. // Close the device @@ -1564,37 +1607,37 @@ public abstract class DfuBaseService extends IntentService { } catch (final UnknownResponseException e) { final int error = ERROR_INVALID_RESPONSE; loge(e.getMessage()); - sendLogBroadcast(Level.ERROR, e.getMessage()); + sendLogBroadcast(LOG_LEVEL_ERROR, e.getMessage()); logi("Sending Reset command (Op Code = 6)"); writeOpCode(gatt, controlPointCharacteristic, OP_CODE_RESET); - sendLogBroadcast(Level.APPLICATION, "Reset request sent"); + 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(Level.ERROR, String.format("Remote DFU error: %s", GattError.parse(error))); + 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(Level.APPLICATION, "Reset request sent"); + sendLogBroadcast(LOG_LEVEL_APPLICATION, "Reset request sent"); terminateConnection(gatt, error); } } catch (final UploadAbortedException e) { logi("Upload aborted"); - sendLogBroadcast(Level.WARNING, "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(Level.APPLICATION, "Reset request sent"); + sendLogBroadcast(LOG_LEVEL_APPLICATION, "Reset request sent"); } catch (final Exception e1) { // do nothing } terminateConnection(gatt, PROGRESS_ABORTED); } catch (final DeviceDisconnectedException e) { - sendLogBroadcast(Level.ERROR, "Device has disconnected"); + sendLogBroadcast(LOG_LEVEL_ERROR, "Device has disconnected"); // TODO reconnect n times? loge(e.getMessage()); if (mNotificationsEnabled) @@ -1603,13 +1646,13 @@ public abstract class DfuBaseService extends IntentService { updateProgressNotification(ERROR_DEVICE_DISCONNECTED); } catch (final DfuException e) { final int error = e.getErrorNumber() & ~ERROR_CONNECTION_MASK; - sendLogBroadcast(Level.ERROR, String.format("Error (0x%02X): %s", error, GattError.parse(error))); + 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(Level.APPLICATION, "Reset request sent"); + sendLogBroadcast(LOG_LEVEL_APPLICATION, "Reset request sent"); } catch (final Exception e1) { // do nothing } @@ -1721,11 +1764,11 @@ public abstract class DfuBaseService extends IntentService { if (mConnectionState != STATE_DISCONNECTED) { updateProgressNotification(PROGRESS_DISCONNECTING); - // No need to disable notifications + // No need to disable notifications // Disconnect from the device disconnect(gatt); - sendLogBroadcast(Level.INFO, "Disconnected"); + sendLogBroadcast(LOG_LEVEL_INFO, "Disconnected"); } // Close the device @@ -1774,7 +1817,7 @@ public abstract class DfuBaseService extends IntentService { */ private void close(final BluetoothGatt gatt) { logi("Cleaning up..."); - sendLogBroadcast(Level.DEBUG, "gatt.close()"); + sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.close()"); gatt.close(); mConnectionState = STATE_CLOSED; } @@ -1789,10 +1832,10 @@ public abstract class DfuBaseService extends IntentService { /* * 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 is unable to get the information about changing services. The hidden refresh method may be used to force refreshing the device cache. + * If not bonded the Android is unable to get the information about changing services. The hidden refresh method may be used to force refreshing the device cache. */ if (force || gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE) { - sendLogBroadcast(Level.DEBUG, "gatt.refresh()"); + 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. */ @@ -1804,7 +1847,7 @@ public abstract class DfuBaseService extends IntentService { } } catch (Exception e) { loge("An exception occurred while refreshing device", e); - sendLogBroadcast(Level.WARNING, "Refreshing failed"); + sendLogBroadcast(LOG_LEVEL_WARNING, "Refreshing failed"); } } } @@ -1844,7 +1887,7 @@ public abstract class DfuBaseService extends IntentService { mError = 0; logi("Reading DFU version number..."); - sendLogBroadcast(Level.VERBOSE, "Reading DFU version number..."); + sendLogBroadcast(LOG_LEVEL_VERBOSE, "Reading DFU version number..."); gatt.readCharacteristic(characteristic); @@ -1890,7 +1933,7 @@ public abstract class DfuBaseService extends IntentService { return; logi("Enabling " + debugString + "..."); - sendLogBroadcast(Level.VERBOSE, "Enabling " + debugString + " for " + characteristic.getUuid()); + sendLogBroadcast(LOG_LEVEL_VERBOSE, "Enabling " + debugString + " for " + characteristic.getUuid()); // enable notifications locally gatt.setCharacteristicNotification(characteristic, true); @@ -1898,7 +1941,7 @@ public abstract class DfuBaseService extends IntentService { // 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(Level.DEBUG, "gatt.writeDescriptor(" + descriptor.getUuid() + (type == NOTIFICATIONS ? ", value=0x01-00)" : ", value=0x02-00)")); + 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 gets disconnected or an error occur @@ -1961,8 +2004,8 @@ public abstract class DfuBaseService extends IntentService { mResetRequestSent = reset; characteristic.setValue(value); - sendLogBroadcast(Level.VERBOSE, "Writing to characteristic " + characteristic.getUuid()); - sendLogBroadcast(Level.DEBUG, "gatt.writeCharacteristic(" + characteristic.getUuid() + ")"); + 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 @@ -2002,8 +2045,8 @@ public abstract class DfuBaseService extends IntentService { characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); characteristic.setValue(new byte[4]); characteristic.setValue(imageSize, BluetoothGattCharacteristic.FORMAT_UINT32, 0); - sendLogBroadcast(Level.VERBOSE, "Writing to characteristic " + characteristic.getUuid()); - sendLogBroadcast(Level.DEBUG, "gatt.writeCharacteristic(" + characteristic.getUuid() + ")"); + 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 @@ -2053,8 +2096,8 @@ public abstract class DfuBaseService extends IntentService { characteristic.setValue(softDeviceImageSize, BluetoothGattCharacteristic.FORMAT_UINT32, 0); characteristic.setValue(bootloaderImageSize, BluetoothGattCharacteristic.FORMAT_UINT32, 4); characteristic.setValue(appImageSize, BluetoothGattCharacteristic.FORMAT_UINT32, 8); - sendLogBroadcast(Level.VERBOSE, "Writing to characteristic " + characteristic.getUuid()); - sendLogBroadcast(Level.DEBUG, "gatt.writeCharacteristic(" + characteristic.getUuid() + ")"); + 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 @@ -2100,8 +2143,8 @@ public abstract class DfuBaseService extends IntentService { characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); characteristic.setValue(locBuffer); logi("Sending init packet (Value = " + parse(locBuffer) + ")"); - sendLogBroadcast(Level.VERBOSE, "Writing to characteristic " + characteristic.getUuid()); - sendLogBroadcast(Level.DEBUG, "gatt.writeCharacteristic(" + characteristic.getUuid() + ")"); + 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 @@ -2140,7 +2183,7 @@ public abstract class DfuBaseService extends IntentService { final byte[] buffer = mBuffer; try { final int size = inputStream.read(buffer); - sendLogBroadcast(Level.VERBOSE, "Sending firmware to characteristic " + packetCharacteristic.getUuid() + "..."); + 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); @@ -2185,7 +2228,7 @@ public abstract class DfuBaseService extends IntentService { // 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. } @@ -2209,9 +2252,9 @@ public abstract class DfuBaseService extends IntentService { boolean result; mRequestCompleted = false; - sendLogBroadcast(Level.VERBOSE, "Starting pairing..."); + sendLogBroadcast(LOG_LEVEL_VERBOSE, "Starting pairing..."); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - sendLogBroadcast(Level.DEBUG, "gatt.getDevice().createBond()"); + sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.getDevice().createBond()"); result = device.createBond(); } else { result = createBondApi18(device); @@ -2236,7 +2279,7 @@ public abstract class DfuBaseService extends IntentService { try { final Method createBond = device.getClass().getMethod("createBond"); if (createBond != null) { - sendLogBroadcast(Level.DEBUG, "gatt.getDevice().createBond() (hidden)"); + sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.getDevice().createBond() (hidden)"); return (Boolean) createBond.invoke(device); } } catch (final Exception e) { @@ -2255,7 +2298,7 @@ public abstract class DfuBaseService extends IntentService { if (device.getBondState() == BluetoothDevice.BOND_NONE) return true; - sendLogBroadcast(Level.VERBOSE, "Removing bond information..."); + 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. @@ -2264,7 +2307,7 @@ public abstract class DfuBaseService extends IntentService { final Method removeBond = device.getClass().getMethod("removeBond"); if (removeBond != null) { mRequestCompleted = false; - sendLogBroadcast(Level.DEBUG, "gatt.getDevice().removeBond() (hidden)"); + sendLogBroadcast(LOG_LEVEL_DEBUG, "gatt.getDevice().removeBond() (hidden)"); result = (Boolean) removeBond.invoke(device); // We have to wait until device is unbounded @@ -2313,11 +2356,6 @@ public abstract class DfuBaseService extends IntentService { return mReceivedData; } - /** - * Stores the last progress percent. Used to lower number of calls of {@link #updateProgressNotification(int)}. - */ - private int mLastProgress = -1; - /** * Creates or updates the notification in the Notification Manager. Sends broadcast with current progress to the activity. */ @@ -2396,8 +2434,6 @@ public abstract class DfuBaseService extends IntentService { intent.putExtra(EXTRA_DEVICE_ADDRESS, deviceAddress); intent.putExtra(EXTRA_DEVICE_NAME, deviceName); intent.putExtra(EXTRA_PROGRESS, progress); // this may contains ERROR_CONNECTION_MASK bit! - if (mLogSession != null) - intent.putExtra(EXTRA_LOG_URI, mLogSession.getSessionUri()); final PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pendingIntent); @@ -2421,10 +2457,6 @@ public abstract class DfuBaseService extends IntentService { *
      • {@link #EXTRA_DEVICE_NAME} - target device name
      • *
      • {@link #EXTRA_PROGRESS} - the connection state (values < 0)*, current progress (0-100) or error number if {@link #ERROR_MASK} bit set.
      • *
      - * If the nRF Logger session {@link android.net.Uri} was passed to create a service it will also be added as an extra: - *
        - *
      • {@link #EXTRA_LOG_URI} - the log session Uri
      • - *
      *

      * __________
      * * - connection state constants: @@ -2457,8 +2489,6 @@ public abstract class DfuBaseService extends IntentService { broadcast.putExtra(EXTRA_PARTS_TOTAL, mPartsTotal); broadcast.putExtra(EXTRA_SPEED_B_PER_MS, speed); broadcast.putExtra(EXTRA_AVG_SPEED_B_PER_MS, avgSpeed); - if (mLogSession != null) - broadcast.putExtra(EXTRA_LOG_URI, mLogSession.getSessionUri()); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } @@ -2466,25 +2496,16 @@ public abstract class DfuBaseService extends IntentService { final Intent broadcast = new Intent(BROADCAST_ERROR); broadcast.putExtra(EXTRA_DATA, error & ~ERROR_CONNECTION_MASK); broadcast.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress); - if (mLogSession != null) - broadcast.putExtra(EXTRA_LOG_URI, mLogSession.getSessionUri()); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } private void sendLogBroadcast(final int level, final String message) { - final ILogSession session = mLogSession; final String fullMessage = "[DFU] " + message; - if (session == null) { - // the log provider is not installed, use broadcast action - 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); - } else { - // the log provider is installed, we can use logger - Logger.log(session, level, fullMessage); - } + 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); } /** @@ -2510,12 +2531,6 @@ public abstract class DfuBaseService extends IntentService { return true; } - private static IntentFilter makeDfuActionIntentFilter() { - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(DfuBaseService.BROADCAST_ACTION); - return intentFilter; - } - private void loge(final String message) { if (BuildConfig.DEBUG) Log.e(TAG, message); diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuSettingsConstants.java b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuSettingsConstants.java index 246616b..f6f483c 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/DfuSettingsConstants.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/DfuSettingsConstants.java @@ -1,3 +1,25 @@ +/************************************************************************************************************************************************* + * 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 interface DfuSettingsConstants { diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/HexInputStream.java b/dfu/src/main/java/no/nordicsemi/android/dfu/HexInputStream.java index f3dfe25..be7c702 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/HexInputStream.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/HexInputStream.java @@ -1,11 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. - * This heading must NOT be removed from the file. - ******************************************************************************/ +/************************************************************************************************************************************************* + * 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 java.io.BufferedInputStream; diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/ZipHexInputStream.java b/dfu/src/main/java/no/nordicsemi/android/dfu/ZipHexInputStream.java index ad2d642..1cd659a 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/ZipHexInputStream.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/ZipHexInputStream.java @@ -1,3 +1,25 @@ +/************************************************************************************************************************************************* + * 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 java.io.ByteArrayOutputStream; diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/DeviceDisconnectedException.java b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/DeviceDisconnectedException.java index b24f89d..f19d14c 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/DeviceDisconnectedException.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/DeviceDisconnectedException.java @@ -1,11 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. - * This heading must NOT be removed from the file. - ******************************************************************************/ +/************************************************************************************************************************************************* + * 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.exception; public class DeviceDisconnectedException extends Exception { diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/DfuException.java b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/DfuException.java index a1caab5..1d51b42 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/DfuException.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/DfuException.java @@ -1,11 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. - * This heading must NOT be removed from the file. - ******************************************************************************/ +/************************************************************************************************************************************************* + * 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.exception; import no.nordicsemi.android.dfu.DfuBaseService; diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/HexFileValidationException.java b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/HexFileValidationException.java index 04305c4..e389268 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/HexFileValidationException.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/HexFileValidationException.java @@ -1,11 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. - * This heading must NOT be removed from the file. - ******************************************************************************/ +/************************************************************************************************************************************************* + * 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.exception; import java.io.IOException; diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/RemoteDfuException.java b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/RemoteDfuException.java index 50edbca..fe5ed67 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/RemoteDfuException.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/RemoteDfuException.java @@ -1,11 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. - * This heading must NOT be removed from the file. - ******************************************************************************/ +/************************************************************************************************************************************************* + * 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.exception; public class RemoteDfuException extends Exception { diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/UnknownResponseException.java b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/UnknownResponseException.java index 28a59bd..813db50 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/UnknownResponseException.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/UnknownResponseException.java @@ -1,11 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. - * This heading must NOT be removed from the file. - ******************************************************************************/ +/************************************************************************************************************************************************* + * 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.exception; public class UnknownResponseException extends Exception { diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/UploadAbortedException.java b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/UploadAbortedException.java index 7c482a1..8f3a02b 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/exception/UploadAbortedException.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/exception/UploadAbortedException.java @@ -1,11 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. - * This heading must NOT be removed from the file. - ******************************************************************************/ +/************************************************************************************************************************************************* + * 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.exception; public class UploadAbortedException extends Exception { diff --git a/dfu/src/main/java/no/nordicsemi/android/error/GattError.java b/dfu/src/main/java/no/nordicsemi/android/error/GattError.java index 47d438c..9082711 100644 --- a/dfu/src/main/java/no/nordicsemi/android/error/GattError.java +++ b/dfu/src/main/java/no/nordicsemi/android/error/GattError.java @@ -1,11 +1,25 @@ -/******************************************************************************* - * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. +/************************************************************************************************************************************************* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. - * This heading must NOT be removed from the file. - ******************************************************************************/ + * 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 no.nordicsemi.android.dfu.DfuBaseService;