Advertisement
Guest User

Untitled

a guest
Mar 21st, 2018
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.82 KB | None | 0 0
  1. @Override
  2. protected void onHandleIntent(final Intent intent) {
  3. // Read input parameters
  4. final String deviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
  5. final String deviceName = intent.getStringExtra(EXTRA_DEVICE_NAME);
  6. final boolean disableNotification = intent.getBooleanExtra(EXTRA_DISABLE_NOTIFICATION, false);
  7. final boolean foregroundService = intent.getBooleanExtra(EXTRA_FOREGROUND_SERVICE, true);
  8. final String filePath = intent.getStringExtra(EXTRA_FILE_PATH);
  9. final Uri fileUri = intent.getParcelableExtra(EXTRA_FILE_URI);
  10. final int fileResId = intent.getIntExtra(EXTRA_FILE_RES_ID, 0);
  11. final String initFilePath = intent.getStringExtra(EXTRA_INIT_FILE_PATH);
  12. final Uri initFileUri = intent.getParcelableExtra(EXTRA_INIT_FILE_URI);
  13. final int initFileResId = intent.getIntExtra(EXTRA_INIT_FILE_RES_ID, 0);
  14. int fileType = intent.getIntExtra(EXTRA_FILE_TYPE, TYPE_AUTO);
  15. if (filePath != null && fileType == TYPE_AUTO)
  16. fileType = filePath.toLowerCase(Locale.US).endsWith("zip") ? TYPE_AUTO : TYPE_APPLICATION;
  17. String mimeType = intent.getStringExtra(EXTRA_FILE_MIME_TYPE);
  18. mimeType = mimeType != null ? mimeType : (fileType == TYPE_AUTO ? MIME_TYPE_ZIP : MIME_TYPE_OCTET_STREAM);
  19.  
  20. // Check file type and mime-type
  21. if ((fileType & ~(TYPE_SOFT_DEVICE | TYPE_BOOTLOADER | TYPE_APPLICATION)) > 0 || !(MIME_TYPE_ZIP.equals(mimeType) || MIME_TYPE_OCTET_STREAM.equals(mimeType))) {
  22. logw("File type or file mime-type not supported");
  23. sendLogBroadcast(LOG_LEVEL_WARNING, "File type or file mime-type not supported");
  24. report(ERROR_FILE_TYPE_UNSUPPORTED);
  25. return;
  26. }
  27. if (MIME_TYPE_OCTET_STREAM.equals(mimeType) && fileType != TYPE_SOFT_DEVICE && fileType != TYPE_BOOTLOADER && fileType != TYPE_APPLICATION) {
  28. logw("Unable to determine file type");
  29. sendLogBroadcast(LOG_LEVEL_WARNING, "Unable to determine file type");
  30. report(ERROR_FILE_TYPE_UNSUPPORTED);
  31. return;
  32. }
  33. if (!disableNotification && getNotificationTarget() == null) {
  34. // This would eventually crash later...
  35. throw new NullPointerException("getNotificationTarget() must not return null if notifications are enabled");
  36. }
  37. if (!foregroundService && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  38. logw("Foreground service disabled. Android Oreo or newer may kill a background service few moments after user closes the application.\n" +
  39. "Consider enabling foreground service using DfuServiceInitiator#setForeground(boolean)");
  40. }
  41. UuidHelper.assignCustomUuids(intent);
  42.  
  43. mDeviceAddress = deviceAddress;
  44. mDeviceName = deviceName;
  45. mDisableNotification = disableNotification;
  46. mConnectionState = STATE_DISCONNECTED;
  47. mError = 0;
  48.  
  49. // 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.
  50. // 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.
  51. final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
  52. final String value = preferences.getString(DfuSettingsConstants.SETTINGS_MBR_SIZE, String.valueOf(DfuSettingsConstants.SETTINGS_DEFAULT_MBR_SIZE));
  53. int mbrSize;
  54. try {
  55. mbrSize = Integer.parseInt(value);
  56. if (mbrSize < 0)
  57. mbrSize = 0;
  58. } catch (final NumberFormatException e) {
  59. mbrSize = DfuSettingsConstants.SETTINGS_DEFAULT_MBR_SIZE;
  60. }
  61.  
  62. if (foregroundService) {
  63. startForeground();
  64. }
  65. sendLogBroadcast(LOG_LEVEL_VERBOSE, "DFU service started");
  66.  
  67. /*
  68. * First the service is trying to read the firmware and init packet files.
  69. */
  70. InputStream is = mFirmwareInputStream;
  71. InputStream initIs = mInitFileInputStream;
  72. try {
  73. final boolean firstRun = mFirmwareInputStream == null;
  74.  
  75. // Prepare data to send, calculate stream size
  76. try {
  77. if (firstRun) {
  78. // The files are opened only once, when DFU service is first started.
  79. // In case the service needs to be restarted (for example a buttonless service
  80. // was found or to send Application in the second connection) the input stream
  81. // is kept as a global service field. This is to avoid SecurityException
  82. // when the URI was granted with one-time read permission.
  83. // See: Intent#FLAG_GRANT_READ_URI_PERMISSION (https://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMISSION).
  84. sendLogBroadcast(LOG_LEVEL_VERBOSE, "Opening file...");
  85. if (fileUri != null) {
  86. is = openInputStream(fileUri, mimeType, mbrSize, fileType);
  87. } else if (filePath != null) {
  88. is = openInputStream(filePath, mimeType, mbrSize, fileType);
  89. } else if (fileResId > 0) {
  90. is = openInputStream(fileResId, mimeType, mbrSize, fileType);
  91. }
  92.  
  93. // The Init file Input Stream is kept global only in case it was provided
  94. // as an argument (separate file for HEX/BIN and DAT files).
  95. // If a ZIP file was given with DAT file(s) inside it will be taken from the ZIP
  96. // ~20 lines below.
  97. if (initFileUri != null) {
  98. // Try to read the Init Packet file from URI
  99. initIs = getContentResolver().openInputStream(initFileUri);
  100. } else if (initFilePath != null) {
  101. // Try to read the Init Packet file from path
  102. initIs = new FileInputStream(initFilePath);
  103. } else if (initFileResId > 0) {
  104. // Try to read the Init Packet file from given resource
  105. initIs = getResources().openRawResource(initFileResId);
  106. }
  107.  
  108. final int imageSizeInBytes = is.available();
  109. if ((imageSizeInBytes % 4) != 0)
  110. throw new SizeValidationException("The new firmware is not word-aligned.");
  111. }
  112.  
  113. // Update the file type bit field basing on the ZIP content
  114. if (MIME_TYPE_ZIP.equals(mimeType)) {
  115. final ArchiveInputStream zhis = (ArchiveInputStream) is;
  116. if (fileType == TYPE_AUTO) {
  117. fileType = zhis.getContentType();
  118. } else {
  119. fileType = zhis.setContentType(fileType);
  120. }
  121.  
  122. // Validate sizes
  123. if ((fileType & TYPE_APPLICATION) > 0 && (zhis.applicationImageSize() % 4) != 0)
  124. throw new SizeValidationException("Application firmware is not word-aligned.");
  125. if ((fileType & TYPE_BOOTLOADER) > 0 && (zhis.bootloaderImageSize() % 4) != 0)
  126. throw new SizeValidationException("Bootloader firmware is not word-aligned.");
  127. if ((fileType & TYPE_SOFT_DEVICE) > 0 && (zhis.softDeviceImageSize() % 4) != 0)
  128. throw new SizeValidationException("Soft Device firmware is not word-aligned.");
  129.  
  130. if (fileType == TYPE_APPLICATION) {
  131. if (zhis.getApplicationInit() != null)
  132. initIs = new ByteArrayInputStream(zhis.getApplicationInit());
  133. } else {
  134. if (zhis.getSystemInit() != null)
  135. initIs = new ByteArrayInputStream(zhis.getSystemInit());
  136. }
  137. }
  138. mFirmwareInputStream = is;
  139. mInitFileInputStream = initIs;
  140. sendLogBroadcast(LOG_LEVEL_INFO, "Firmware file opened successfully");
  141. } catch (final SecurityException e) {
  142. loge("A security exception occurred while opening file", e);
  143. sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: Permission required");
  144. report(ERROR_FILE_NOT_FOUND);
  145. return;
  146. } catch (final FileNotFoundException e) {
  147. loge("An exception occurred while opening file", e);
  148. sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: File not found");
  149. report(ERROR_FILE_NOT_FOUND);
  150. return;
  151. } catch (final SizeValidationException e) {
  152. loge("Firmware not word-aligned", e);
  153. sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: Firmware size must be word-aligned");
  154. report(ERROR_FILE_SIZE_INVALID);
  155. return;
  156. } catch (final IOException e) {
  157. loge("An exception occurred while calculating file size", e);
  158. sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: " + e.getLocalizedMessage());
  159. report(ERROR_FILE_ERROR);
  160. return;
  161. } catch (final Exception e) {
  162. loge("An exception occurred while opening files. Did you set the firmware file?", e);
  163. sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: " + e.getLocalizedMessage());
  164. report(ERROR_FILE_ERROR);
  165. return;
  166. }
  167.  
  168. if (!firstRun) {
  169. // Wait a second... If we were connected before it's good to give some time before we start reconnecting.
  170. waitFor(1000);
  171. // Looks like a second is not enough. The ACL_DISCONNECTED broadcast sometimes comes later (on Android 7.0)
  172. waitFor(1000);
  173. }
  174.  
  175. mProgressInfo = new DfuProgressInfo(this);
  176.  
  177. if (mAborted) {
  178. logw("Upload aborted");
  179. sendLogBroadcast(LOG_LEVEL_WARNING, "Upload aborted");
  180. mProgressInfo.setProgress(PROGRESS_ABORTED);
  181. return;
  182. }
  183.  
  184. /*
  185. * Now let's connect to the device.
  186. * All the methods below are synchronous. The mLock object is used to wait for asynchronous calls.
  187. */
  188. sendLogBroadcast(LOG_LEVEL_VERBOSE, "Connecting to DFU target...");
  189. mProgressInfo.setProgress(PROGRESS_CONNECTING);
  190.  
  191. final BluetoothGatt gatt = connect(deviceAddress);
  192. // Are we connected?
  193. if (gatt == null) {
  194. loge("Bluetooth adapter disabled");
  195. sendLogBroadcast(LOG_LEVEL_ERROR, "Bluetooth adapter disabled");
  196. report(ERROR_BLUETOOTH_DISABLED);
  197. return;
  198. }
  199. if (mConnectionState == STATE_DISCONNECTED) {
  200. if (mError == (ERROR_CONNECTION_STATE_MASK | 133)) {
  201. loge("Device not reachable. Check if the device with address " + deviceAddress + " is in range, is advertising and is connectable");
  202. sendLogBroadcast(LOG_LEVEL_ERROR, "Error 133: Connection timeout");
  203. } else {
  204. loge("Device got disconnected before service discovery finished");
  205. sendLogBroadcast(LOG_LEVEL_ERROR, "Disconnected");
  206. }
  207. terminateConnection(gatt, ERROR_DEVICE_DISCONNECTED);
  208. return;
  209. }
  210. if (mError > 0) { // error occurred
  211. if ((mError & ERROR_CONNECTION_STATE_MASK) > 0) {
  212. final int error = mError & ~ERROR_CONNECTION_STATE_MASK;
  213. loge("An error occurred while connecting to the device:" + error);
  214. sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Connection failed (0x%02X): %s", error, GattError.parseConnectionError(error)));
  215. } else {
  216. final int error = mError & ~ERROR_CONNECTION_MASK;
  217. loge("An error occurred during discovering services:" + error);
  218. sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Connection failed (0x%02X): %s", error, GattError.parse(error)));
  219. }
  220. // Connection usually fails due to a 133 error (device unreachable, or.. something else went wrong).
  221. // Usually trying the same for the second time works.
  222. if (intent.getIntExtra(EXTRA_ATTEMPT, 0) == 0) {
  223. sendLogBroadcast(LOG_LEVEL_WARNING, "Retrying...");
  224.  
  225. if (mConnectionState != STATE_DISCONNECTED) {
  226. // Disconnect from the device
  227. disconnect(gatt);
  228. }
  229. // Close the device
  230. refreshDeviceCache(gatt, true);
  231. close(gatt);
  232.  
  233. logi("Restarting the service");
  234. final Intent newIntent = new Intent();
  235. newIntent.fillIn(intent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_PACKAGE);
  236. newIntent.putExtra(EXTRA_ATTEMPT, 1);
  237. startService(newIntent);
  238. return;
  239. }
  240. terminateConnection(gatt, mError);
  241. return;
  242. }
  243. if (mAborted) {
  244. logw("Upload aborted");
  245. sendLogBroadcast(LOG_LEVEL_WARNING, "Upload aborted");
  246. terminateConnection(gatt, 0);
  247. mProgressInfo.setProgress(PROGRESS_ABORTED);
  248. return;
  249. }
  250. sendLogBroadcast(LOG_LEVEL_INFO, "Services discovered");
  251.  
  252. // Reset the attempt counter
  253. intent.putExtra(EXTRA_ATTEMPT, 0);
  254.  
  255. DfuService dfuService = null;
  256. try {
  257. /*
  258. * Device services were discovered. Based on them we may now choose the implementation.
  259. */
  260. final DfuServiceProvider serviceProvider = new DfuServiceProvider();
  261. mDfuServiceImpl = serviceProvider; // This is required if the provider is now able read data from the device
  262. mDfuServiceImpl = dfuService = serviceProvider.getServiceImpl(intent, this, gatt);
  263. if (dfuService == null) {
  264. Log.w(TAG, "DFU Service not found.");
  265. sendLogBroadcast(LOG_LEVEL_WARNING, "DFU Service not found");
  266. terminateConnection(gatt, ERROR_SERVICE_NOT_FOUND);
  267. return;
  268. }
  269.  
  270. // Begin the DFU depending on the implementation
  271. if (dfuService.initialize(intent, gatt, fileType, is, initIs)) {
  272. dfuService.performDfu(intent);
  273. }
  274. } catch (final UploadAbortedException e) {
  275. logw("Upload aborted");
  276. sendLogBroadcast(LOG_LEVEL_WARNING, "Upload aborted");
  277. terminateConnection(gatt, 0);
  278. mProgressInfo.setProgress(PROGRESS_ABORTED);
  279. } catch (final DeviceDisconnectedException e) {
  280. sendLogBroadcast(LOG_LEVEL_ERROR, "Device has disconnected");
  281. // TODO reconnect n times?
  282. loge(e.getMessage());
  283. close(gatt);
  284. report(ERROR_DEVICE_DISCONNECTED);
  285. } catch (final DfuException e) {
  286. int error = e.getErrorNumber();
  287. // Connection state errors and other Bluetooth GATT callbacks share the same error numbers. Therefore we are using bit masks to identify the type.
  288. if ((error & ERROR_CONNECTION_STATE_MASK) > 0) {
  289. error &= ~ERROR_CONNECTION_STATE_MASK;
  290. sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Error (0x%02X): %s", error, GattError.parseConnectionError(error)));
  291. } else {
  292. error &= ~ERROR_CONNECTION_MASK;
  293. sendLogBroadcast(LOG_LEVEL_ERROR, String.format("Error (0x%02X): %s", error, GattError.parse(error)));
  294. }
  295. loge(e.getMessage());
  296. terminateConnection(gatt, e.getErrorNumber() /* we return the whole error number, including the error type mask */);
  297. } finally {
  298. if (dfuService != null) {
  299. dfuService.release();
  300. }
  301. }
  302. } finally {
  303. if (foregroundService) {
  304. // This will stop foreground state and, if the progress notifications were disabled
  305. // it will also remove the notification indicating foreground service.
  306. stopForeground(disableNotification);
  307. }
  308. }
  309. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement