Java tutorial
/* * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.iot.system.service.api; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.os.BatteryManager; import android.os.Build; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import org.wso2.iot.system.service.R; import org.wso2.iot.system.service.utils.CommonUtils; import org.wso2.iot.system.service.utils.Constants; import org.wso2.iot.system.service.utils.Preference; import java.io.IOException; import java.net.MalformedURLException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; public class OTADownload implements OTAServerManager.OTAStateChangeListener { private static final String TAG = "OTADownload"; private static final String SI_UNITS_INDEX = "kMGTPE"; private static final String BINARY_UNITS_INDEX = "KMGTPE"; private static final String UPGRADE_AVAILABLE = "upgradeAvailable"; private static final String UPGRADE_VERSION = "version"; private static final String UPGRADE_RELEASE = "release"; private static final String UPGRADE_SIZE = "size"; private static final String UPGRADE_DESCRIPTION = "description"; private Context context; private OTAServerManager otaServerManager; public OTADownload(Context context) { this.context = context; Preference.putString(context, context.getResources().getString(R.string.upgrade_download_status), Constants.Status.REQUEST_PLACED); try { otaServerManager = new OTAServerManager(this.context); otaServerManager.setStateChangeListener(this); } catch (MalformedURLException e) { otaServerManager = null; String message = "Firmware upgrade URL provided is not valid."; if (Preference.getBoolean(context, context.getResources().getString(R.string.firmware_status_check_in_progress))) { CommonUtils.sendBroadcast(context, Constants.Operation.GET_FIRMWARE_UPGRADE_PACKAGE_STATUS, Constants.Code.FAILURE, Constants.Status.MALFORMED_OTA_URL, message); } else { CommonUtils.sendBroadcast(context, Constants.Operation.UPGRADE_FIRMWARE, Constants.Code.FAILURE, Constants.Status.MALFORMED_OTA_URL, message); CommonUtils.callAgentApp(context, Constants.Operation.FIRMWARE_UPGRADE_FAILURE, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), message); } Log.e(TAG, "OTA server manager threw exception ..." + e); } } /** * Returns the byte count in a human readable format. * * @param bytes - Bytes to be converted. * @param isSI - True if the input is in SI units and False if the input is in binary units. * @return - Byte count string. */ public String byteCountToDisplaySize(long bytes, boolean isSI) { int unit = isSI ? 1000 : 1024; if (bytes < unit) { return bytes + " B"; } int numberToFormat = (int) (Math.log(bytes) / Math.log(unit)); String prefix = (isSI ? SI_UNITS_INDEX : BINARY_UNITS_INDEX).charAt(numberToFormat - 1) + (isSI ? "" : "i"); return String.format("%.1f %sB", bytes / Math.pow(unit, numberToFormat), prefix); } public void startOTA() { // If the URL is not correctly provided, server manager will be null if (otaServerManager != null) { CommonUtils.sendBroadcast(context, Constants.Operation.UPGRADE_FIRMWARE, Constants.Code.SUCCESS, Constants.Status.REQUEST_PLACED, null); //Check in the main service thread otaServerManager.startCheckingVersion(); } } private int getBatteryLevel(Context context) { Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); int level = 0; if (batteryIntent != null) { level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); } return level; } public void onStateOrProgress(int message, int error, BuildPropParser parser, long info) { /* State change will be 0 -> Checked(1) -> Downloading(2) -> Upgrading(3) */ switch (message) { case STATE_IN_CHECKED: onStateChecked(error, parser); break; case STATE_IN_DOWNLOADING: onStateDownload(error, info); break; case STATE_IN_UPGRADING: onStateUpgrade(error); break; case MESSAGE_DOWNLOAD_PROGRESS: break; case MESSAGE_VERIFY_PROGRESS: onProgress(info); break; } } public boolean checkNetworkOnline() { ConnectivityManager connectivityManager = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = connectivityManager.getActiveNetworkInfo(); boolean status = false; if (info != null && info.isConnectedOrConnecting()) { status = true; } return status; } public void onStateChecked(int error, final BuildPropParser parser) { final String operation = Preference.getBoolean(context, context.getResources().getString(R.string.firmware_status_check_in_progress)) ? Constants.Operation.GET_FIRMWARE_UPGRADE_PACKAGE_STATUS : Constants.Operation.UPGRADE_FIRMWARE; if (error == 0) { if (!otaServerManager.compareLocalVersionToServer(parser)) { JSONObject result = new JSONObject(); try { result.put(UPGRADE_AVAILABLE, false); if (parser != null) { result.put(UPGRADE_DESCRIPTION, parser.getProp("Software is up to date")); } CommonUtils.sendBroadcast(context, operation, Constants.Code.SUCCESS, Constants.Status.NO_UPGRADE_FOUND, result.toString()); } catch (JSONException e) { String message = "Result payload build failed."; CommonUtils.sendBroadcast(context, operation, Constants.Code.FAILURE, Constants.Status.UPDATE_INFO_NOT_READABLE, message); Log.e(TAG, message + e); } String message = "Software is up to date:" + Build.VERSION.RELEASE + ", " + Build.ID; Log.i(TAG, message); CommonUtils.callAgentApp(context, Constants.Operation.FIRMWARE_UPGRADE_COMPLETE, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), "No upgrades available. " + message); } else if (checkNetworkOnline()) { new AsyncTask<Void, Void, Long>() { protected Long doInBackground(Void... param) { URL url = otaServerManager.getServerConfig().getPackageURL(); URLConnection con; try { con = url.openConnection(); con.setConnectTimeout(Constants.FIRMWARE_UPGRADE_CONNECTIVITY_TIMEOUT); con.setReadTimeout(Constants.FIRMWARE_UPGRADE_READ_TIMEOUT); return (long) con.getContentLength(); } catch (SocketTimeoutException e) { String message = "Connection failure (Socket timeout) when retrieving update package size."; Log.e(TAG, message + e); CommonUtils.sendBroadcast(context, operation, Constants.Code.FAILURE, Constants.Status.CONNECTION_FAILED, message); CommonUtils .callAgentApp(context, Constants.Operation.FAILED_FIRMWARE_UPGRADE_NOTIFICATION, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), message); return (long) -1; } catch (IOException e) { String message = "Connection failure when retrieving update package size."; Log.e(TAG, message + e); CommonUtils.sendBroadcast(context, operation, Constants.Code.FAILURE, Constants.Status.CONNECTION_FAILED, message); CommonUtils .callAgentApp(context, Constants.Operation.FAILED_FIRMWARE_UPGRADE_NOTIFICATION, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), message); return (long) -1; } } protected void onPostExecute(Long bytes) { Log.i(TAG, "New release found " + Build.VERSION.RELEASE + ", " + Build.ID); String length = "Unknown"; if (bytes > 0) { length = byteCountToDisplaySize(bytes, false); } Log.i(TAG, "version :" + parser.getProp("ro.build.id") + "\n" + "full_version :" + parser.getProp("ro.build.description") + "\n" + "size : " + length); //Downloading the new update package if a new version is available. if (Preference.getBoolean(context, context.getResources().getString(R.string.firmware_status_check_in_progress))) { JSONObject result = new JSONObject(); try { result.put(UPGRADE_AVAILABLE, true); result.put(UPGRADE_SIZE, length); result.put(UPGRADE_RELEASE, parser.getNumRelease()); result.put(UPGRADE_VERSION, parser.getProp("ro.build.id")); result.put(UPGRADE_DESCRIPTION, parser.getProp("ro.build.description")); CommonUtils.sendBroadcast(context, Constants.Operation.GET_FIRMWARE_UPGRADE_PACKAGE_STATUS, Constants.Code.SUCCESS, Constants.Status.SUCCESSFUL, result.toString()); } catch (JSONException e) { String message = "Result payload build failed."; CommonUtils.sendBroadcast(context, Constants.Operation.GET_FIRMWARE_UPGRADE_PACKAGE_STATUS, Constants.Code.FAILURE, Constants.Status.OTA_IMAGE_VERIFICATION_FAILED, message); Log.e(TAG, message + e); } } else { Boolean isAutomaticRetry = !Preference.hasPreferenceKey(context, context.getResources().getString(R.string.firmware_upgrade_automatic_retry)) || Preference.getBoolean(context, context.getResources() .getString(R.string.firmware_upgrade_automatic_retry)); if (checkNetworkOnline()) { if (getBatteryLevel( context) >= Constants.REQUIRED_BATTERY_LEVEL_TO_FIRMWARE_UPGRADE) { otaServerManager.startDownloadUpgradePackage(otaServerManager); } else if (isAutomaticRetry) { String message = "Upgrade download has been differed due to insufficient battery level."; Log.w(TAG, message); Preference.putString(context, context.getResources().getString(R.string.upgrade_download_status), Constants.Status.BATTERY_LEVEL_INSUFFICIENT_TO_DOWNLOAD); CommonUtils.sendBroadcast(context, Constants.Operation.UPGRADE_FIRMWARE, Constants.Code.PENDING, Constants.Status.BATTERY_LEVEL_INSUFFICIENT_TO_DOWNLOAD, message); } else { String message = "Upgrade download has been failed due to insufficient battery level."; Preference.putString(context, context.getResources().getString(R.string.upgrade_download_status), Constants.Status.BATTERY_LEVEL_INSUFFICIENT_TO_DOWNLOAD); Log.e(TAG, message); CommonUtils.sendBroadcast(context, Constants.Operation.UPGRADE_FIRMWARE, Constants.Code.FAILURE, Constants.Status.BATTERY_LEVEL_INSUFFICIENT_TO_DOWNLOAD, message); CommonUtils.callAgentApp(context, Constants.Operation.FIRMWARE_UPGRADE_FAILURE, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), message); } } else { String message = "Connection failure when starting upgrade download."; Log.e(TAG, message); if (isAutomaticRetry) { Preference.putString(context, context.getResources().getString(R.string.upgrade_download_status), Constants.Status.NETWORK_UNREACHABLE); CommonUtils.sendBroadcast(context, Constants.Operation.UPGRADE_FIRMWARE, Constants.Code.PENDING, Constants.Status.NETWORK_UNREACHABLE, message); CommonUtils.callAgentApp(context, Constants.Operation.FAILED_FIRMWARE_UPGRADE_NOTIFICATION, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), message); } else { CommonUtils.sendBroadcast(context, Constants.Operation.UPGRADE_FIRMWARE, Constants.Code.FAILURE, Constants.Status.NETWORK_UNREACHABLE, message); CommonUtils.callAgentApp(context, Constants.Operation.FIRMWARE_UPGRADE_FAILURE, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), message); } } } } }.execute(); } else { String message = "Connection failure when starting build prop download."; Log.e(TAG, message); CommonUtils.sendBroadcast(context, operation, Constants.Code.FAILURE, Constants.Status.CONNECTION_FAILED, message); CommonUtils.callAgentApp(context, Constants.Operation.FAILED_FIRMWARE_UPGRADE_NOTIFICATION, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), null); } } else if (error == ERROR_WIFI_NOT_AVAILABLE) { Preference.putString(context, context.getResources().getString(R.string.upgrade_download_status), Constants.Status.WIFI_OFF); Log.e(TAG, "OTA failed due to WIFI connection failure."); } else if (error == ERROR_CANNOT_FIND_SERVER) { String message = "OTA failed due to OTA server not accessible."; Log.e(TAG, message); } else if (error == ERROR_WRITE_FILE_ERROR) { String message = "OTA failed due to file write error."; Log.e(TAG, message); } } public void onStateDownload(int error, Object info) { Log.i(TAG, "Printing package information " + info.toString()); if (error == ERROR_CANNOT_FIND_SERVER) { Log.e(TAG, "Error: server does not have an upgrade package."); } else if (error == ERROR_WRITE_FILE_ERROR) { Log.e(TAG, "OTA failed due to file write error."); } if (error == 0) { // Success download, trying to install package. otaServerManager.startVerifyUpgradePackage(); try { Thread.sleep(10000); } catch (InterruptedException e) { Log.e(TAG, "Thread interrupted." + e); } finally { if (!Preference.getBoolean(context, context.getResources().getString(R.string.verification_failed_flag))) { otaServerManager.startInstallUpgradePackage(); } } } } public void onStateUpgrade(int error) { String operation = Preference.getBoolean(context, context.getResources().getString(R.string.firmware_status_check_in_progress)) ? Constants.Operation.GET_FIRMWARE_UPGRADE_PACKAGE_STATUS : Constants.Operation.UPGRADE_FIRMWARE; if (error == ERROR_PACKAGE_VERIFY_FAILED) { String message = "Package verification failed, signature does not match."; Log.e(TAG, message); Preference.putBoolean(context, context.getResources().getString(R.string.verification_failed_flag), true); CommonUtils.sendBroadcast(context, operation, Constants.Code.FAILURE, Constants.Status.OTA_IMAGE_VERIFICATION_FAILED, message); CommonUtils.callAgentApp(context, Constants.Operation.FIRMWARE_UPGRADE_FAILURE, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), message); } else if (error == ERROR_PACKAGE_INSTALL_FAILED) { String message = "Package installation Failed."; Log.e(TAG, message); CommonUtils.sendBroadcast(context, operation, Constants.Code.FAILURE, Constants.Status.OTA_IMAGE_INSTALL_FAILED, message); CommonUtils.callAgentApp(context, Constants.Operation.FIRMWARE_UPGRADE_FAILURE, Preference.getInt(context, context.getResources().getString(R.string.operation_id)), message); } } public void onProgress(Long progress) { Log.v(TAG, "Progress : " + progress); } }