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.agent.api; import android.app.DownloadManager; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.Browser; import android.support.annotation.RequiresApi; import android.support.v4.content.FileProvider; import android.util.Base64; import android.util.Log; import com.android.volley.AuthFailureError; import com.android.volley.DefaultRetryPolicy; import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.HttpHeaderParser; import org.wso2.iot.agent.AndroidAgentException; import org.wso2.iot.agent.R; import org.wso2.iot.agent.beans.AppInstallRequest; import org.wso2.iot.agent.beans.DeviceAppInfo; import org.wso2.iot.agent.beans.Operation; import org.wso2.iot.agent.proxy.IDPTokenManagerException; import org.wso2.iot.agent.proxy.utils.ServerUtilities; import org.wso2.iot.agent.utils.AlarmUtils; import org.wso2.iot.agent.utils.AppInstallRequestUtil; import org.wso2.iot.agent.utils.CommonUtils; import org.wso2.iot.agent.utils.Constants; import org.wso2.iot.agent.utils.Preference; import org.wso2.iot.agent.utils.StreamHandler; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This class handles all the functionalities required for managing application * installation and un-installation. */ public class ApplicationManager { private static final int SYSTEM_APPS_DISABLED_FLAG = 0; private static final int MAX_URL_HASH = 32; private static final int COMPRESSION_LEVEL = 100; private static final int BUFFER_SIZE = 1024; private static final int READ_FAILED = -1; private static final int BUFFER_OFFSET = 0; private static final int DOWNLOAD_PERCENTAGE_TOTAL = 100; private static final int DOWNLOADER_INCREMENT = 10; private static final String ACTION_INSTALL_COMPLETE = "INSTALL_COMPLETED"; private static final String APP_STATE_DOWNLOAD_STARTED = "DOWNLOAD_STARTED"; private static final String APP_STATE_DOWNLOAD_COMPLETED = "DOWNLOAD_COMPLETED"; private static final String APP_STATE_DOWNLOAD_FAILED = "DOWNLOAD_FAILED"; private static final String APP_STATE_INSTALL_FAILED = "INSTALL_FAILED"; private static final String APP_STATE_INSTALLED = "INSTALLED"; private static final String APP_STATE_UNINSTALLED = "UNINSTALLED"; private static final String APP_STATE_UNINSTALLED_FAILED = "UNINSTALL_FAILED"; private static final String TAG = ApplicationManager.class.getName(); private static final String APP_INSTALLATION_ATTEMPT = "APP_INSTALLATION_ATTEMPT"; private static volatile boolean downloadOngoing = false; private Context context; private Resources resources; private PackageManager packageManager; private DevicePolicyManager policyManager; private long downloadReference; private String appUrl; private BroadcastReceiver downloadReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if (downloadReference == referenceId) { String downloadDirectoryPath = Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); File file = new File(downloadDirectoryPath, resources.getString(R.string.download_mgr_download_file_name)); if (file.exists()) { Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources().getString(R.string.app_status_value_download_completed)); PackageManager pm = context.getPackageManager(); PackageInfo info = pm.getPackageArchiveInfo( downloadDirectoryPath + File.separator + resources.getString(R.string.download_mgr_download_file_name), PackageManager.GET_ACTIVITIES); if (info != null && info.packageName != null) { Preference.putString(context, context.getResources().getString(R.string.shared_pref_installed_app), info.packageName); } Preference.putString(context, context.getResources().getString(R.string.shared_pref_installed_file), resources.getString(R.string.download_mgr_download_file_name)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", new File(downloadDirectoryPath + File.separator + resources.getString(R.string.download_mgr_download_file_name))); triggerInstallation(apkURI); } else { triggerInstallation(Uri.fromFile(new File(downloadDirectoryPath + File.separator + resources.getString(R.string.download_mgr_download_file_name)))); } } else { Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources().getString(R.string.app_status_value_download_failed)); Preference.putString(context, context.getResources().getString(R.string.app_install_failed_message), "App file creation failed on the device."); } } } }; public ApplicationManager(Context context) { this.context = context; this.resources = context.getResources(); this.packageManager = context.getPackageManager(); this.policyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); } /** * Returns a list of all the applications installed on the device. * * @return - List of applications which installed on the device. */ public Map<String, DeviceAppInfo> getInstalledApps() { Map<String, DeviceAppInfo> appList = new HashMap<>(); List<PackageInfo> packages = packageManager.getInstalledPackages(SYSTEM_APPS_DISABLED_FLAG); DeviceAppInfo app; for (PackageInfo packageInfo : packages) { if (Constants.ALLOW_SYSTEM_APPS_IN_APPS_LIST_RESPONSE) { app = new DeviceAppInfo(); app.setAppname(packageInfo.applicationInfo.loadLabel(packageManager).toString()); app.setPackagename(packageInfo.packageName); app.setVersionName(packageInfo.versionName); app.setVersionCode(packageInfo.versionCode); app.setIsSystemApp(isSystemPackage(packageInfo)); app.setIsRunning(isAppRunning(packageInfo.packageName)); appList.put(packageInfo.packageName, app); } else if (!isSystemPackage(packageInfo)) { app = new DeviceAppInfo(); app.setAppname(packageInfo.applicationInfo.loadLabel(packageManager).toString()); app.setPackagename(packageInfo.packageName); app.setVersionName(packageInfo.versionName); app.setVersionCode(packageInfo.versionCode); app.setIsSystemApp(false); app.setIsRunning(isAppRunning(packageInfo.packageName)); appList.put(packageInfo.packageName, app); } } return appList; } public boolean isAppRunning(String packageName) { boolean isRunning = false; try { Process process = Runtime.getRuntime().exec("ps"); BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); int read; char[] buffer = new char[4096]; StringBuffer output = new StringBuffer(); while ((read = in.read(buffer)) > 0) { output.append(buffer, 0, read); if (output.toString().contains(packageName)) { isRunning = true; } } in.close(); if (output.toString().contains(packageName)) { isRunning = true; } } catch (IOException e) { Log.e(TAG, "Running processes shell command failed execution." + e); } finally { return isRunning; } } /** * Returns a list of all the applications installed on the device by user. * * @return - List of applications which installed on the device by user. */ public List<String> getInstalledAppsByUser() { List<String> packagesInstalledByUser = new ArrayList<>(); int flags = PackageManager.GET_META_DATA; List<ApplicationInfo> applications = packageManager.getInstalledApplications(flags); for (ApplicationInfo appInfo : applications) { if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1) { packagesInstalledByUser.add(appInfo.packageName); } } return packagesInstalledByUser; } /** * Return the list of the package names of the apps(hidden and visible) that are user owned * * @return - list of package names of the apps that are not system apps */ public List<String> getAppsOfUser() { List<String> packagesInstalledByUser = new ArrayList<>(); int flags = PackageManager.GET_META_DATA | PackageManager.GET_SHARED_LIBRARY_FILES | PackageManager.GET_UNINSTALLED_PACKAGES; List<ApplicationInfo> applications = packageManager.getInstalledApplications(flags); for (ApplicationInfo appInfo : applications) { if (!((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1)) { packagesInstalledByUser.add(appInfo.packageName); } } return packagesInstalledByUser; } /** * Returns the app name for a particular package name. * * @param packageName - Package name which you need the app name. * @return - Application name. */ public String getAppNameFromPackage(String packageName) { String appName = null; List<PackageInfo> packages = packageManager.getInstalledPackages(SYSTEM_APPS_DISABLED_FLAG); for (PackageInfo packageInfo : packages) { if (packageName.equals(packageInfo.packageName)) { appName = packageInfo.applicationInfo.loadLabel(packageManager).toString(); break; } } return appName; } public boolean isPackageInstalled(String packagename) { try { PackageInfo packageInfo = packageManager.getPackageInfo(packagename, 0); if (packageInfo != null) { return true; } } catch (PackageManager.NameNotFoundException e) { return false; } return false; } private void triggerInstallation(Uri fileUri) { if (Constants.SYSTEM_APP_ENABLED) { CommonUtils.callSystemApp(context, Constants.Operation.SILENT_INSTALL_APPLICATION, "", fileUri.toString()); } else { Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources().getString(R.string.app_status_value_installed)); startInstallerIntent(fileUri); } } /** * Installs an application to the device. * * @param fileUri - File URI should be passed in as a String. */ private void startInstallerIntent(Uri fileUri) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && policyManager.isDeviceOwnerApp(Constants.AGENT_PACKAGE)) { Uri packageFileUri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { packageFileUri = convertContentUriToFileUri(fileUri); } else { packageFileUri = fileUri; } installPackage(packageFileUri); } else { boolean isUnknownSourcesDisallowed = Preference.getBoolean(context, Constants.PreferenceFlag.DISALLOW_UNKNOWN_SOURCES); CommonUtils.allowUnknownSourcesForProfile(context, !isUnknownSourcesDisallowed); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setDataAndType(fileUri, resources.getString(R.string.application_mgr_mime)); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); context.startActivity(intent); } else { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(fileUri, resources.getString(R.string.application_mgr_mime)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } } } private Uri convertContentUriToFileUri(Uri contentUri) { String uriString = contentUri.toString(); uriString = "file://" + Environment.getExternalStorageDirectory() + uriString.replace("content://" + Constants.AGENT_PACKAGE + ".provider/external_files", ""); return Uri.parse(uriString); } @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private boolean installPackage(Uri fileUri) { InputStream in; OutputStream out; String packageName = Preference.getString(context, Constants.PreferenceFlag.CURRENT_INSTALLING_APP); try { File application = new File(fileUri.getPath()); in = new FileInputStream(application); PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.setAppPackageName(packageName); // set params int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); out = session.openWrite(Constants.AGENT_PACKAGE, 0, -1); byte[] buffer = new byte[65536]; int c; while ((c = in.read(buffer)) != -1) { out.write(buffer, 0, c); } session.fsync(out); in.close(); out.close(); session.commit(createIntentSender(context, sessionId, packageName)); return true; } catch (IOException e) { Log.e(TAG, "Error occurred while installing application '" + packageName + "'", e); } catch (Exception e) { Log.e(TAG, "Error occurred while installing application '" + packageName + "'", e); } return false; } private static IntentSender createIntentSender(Context context, int sessionId, String packageName) { Intent intent = new Intent(ACTION_INSTALL_COMPLETE); intent.putExtra("packageName", packageName); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, sessionId, intent, 0); return pendingIntent.getIntentSender(); } /** * Returns whether the app is a system app. * * @param packageInfo - Package of the app which you need the status. * @return - App status. */ private boolean isSystemPackage(PackageInfo packageInfo) { return ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); } /** * Returns a base64 encoded string for a particular image. * * @param drawable - Image as a Drawable object. * @return - Base64 encoded value of the drawable. */ public String encodeImage(Drawable drawable) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, COMPRESSION_LEVEL, outStream); byte[] bitmapContent = outStream.toByteArray(); String encodedImage = Base64.encodeToString(bitmapContent, Base64.NO_WRAP); StreamHandler.closeOutputStream(outStream, TAG); return encodedImage; } /** * Installs an application to the device. * * @param url - APK Url should be passed in as a String. * @param schedule - If update/installation is scheduled, schedule information should be passed. * @param operation - App installation operation. */ public void installApp(String url, String schedule, Operation operation) { if (schedule != null && !schedule.trim().isEmpty() && !schedule.equals("undefined") && !schedule.equals("false")) { try { AlarmUtils.setOneTimeAlarm(context, schedule, Constants.Operation.INSTALL_APPLICATION, operation, url, null); } catch (ParseException e) { Log.e(TAG, "One time alarm time string parsing failed." + e); } return; //Will call installApp method again upon alarm. } int operationId = 0; String operationCode = Constants.Operation.INSTALL_APPLICATION; if (operation != null) { operationId = Preference.getInt(context, context.getResources().getString(R.string.app_install_id)); operationCode = Preference.getString(context, context.getResources().getString(R.string.app_install_code)); if (operationId == operation.getId()) { Log.w(TAG, "Ignoring received operation as it has the same operation ID with ongoing operation."); return; //No point of putting same operation again to the pending queue. Hence ignoring. } if (operationId != 0 && operationCode != null) { AppInstallRequest appInstallRequest = new AppInstallRequest(); appInstallRequest.setApplicationOperationId(operation.getId()); appInstallRequest.setApplicationOperationCode(operation.getCode()); appInstallRequest.setAppUrl(url); AppInstallRequestUtil.addPending(context, appInstallRequest); Log.d(TAG, "Added request to pending queue as there is another installation ongoing."); if (!downloadOngoing) { // Probably installation might ongoing int attempt = Preference.getInt(context, APP_INSTALLATION_ATTEMPT); if (attempt >= 1) { Preference.putInt(context, APP_INSTALLATION_ATTEMPT, 0); Preference.putInt(context, context.getResources().getString(R.string.app_install_id), 0); Preference.putString(context, context.getResources().getString(R.string.app_install_code), null); } else { Preference.putInt(context, APP_INSTALLATION_ATTEMPT, ++attempt); } } else { downloadOngoing = false; //Let's check whether it is actually ongoing or not. } return; //Will call installApp method again once current installation completed. } operationId = operation.getId(); operationCode = operation.getCode(); Preference.putInt(context, APP_INSTALLATION_ATTEMPT, 0); } setupAppDownload(url, operationId, operationCode); } /** * Start app download for install on device. * * @param url - APK Url should be passed in as a String. * @param operationId - Id of the operation. * @param operationCode - Requested operation code. */ public void setupAppDownload(String url, int operationId, String operationCode) { Preference.putInt(context, context.getResources().getString(R.string.app_install_id), operationId); Preference.putString(context, context.getResources().getString(R.string.app_install_code), operationCode); if (url.contains(Constants.APP_DOWNLOAD_ENDPOINT) && Constants.APP_MANAGER_HOST != null) { url = url.substring(url.lastIndexOf("/"), url.length()); this.appUrl = Constants.APP_MANAGER_HOST + Constants.APP_DOWNLOAD_ENDPOINT + url; } else { this.appUrl = url; } Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources().getString(R.string.app_status_value_download_started)); if (Constants.DEFAULT_OWNERSHIP == Constants.OWNERSHIP_COSU) { downloadApp(this.appUrl); } else if (isDownloadManagerAvailable(context) && !url.contains(Constants.HTTPS_PROTOCOL)) { IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); context.registerReceiver(downloadReceiver, filter); removeExistingFile(); downloadViaDownloadManager(this.appUrl, resources.getString(R.string.download_mgr_download_file_name)); } else { downloadApp(this.appUrl); } } /** * Removes an application from the device. * * @param packageName - Application package name should be passed in as a String. */ public void uninstallApplication(String packageName, String schedule) throws AndroidAgentException { String packageUriString = packageName; if (packageName != null) { if (!packageName.contains(resources.getString(R.string.application_package_prefix))) { packageUriString = resources.getString(R.string.application_package_prefix) + packageName; } else { packageName = packageName.replace(resources.getString(R.string.application_package_prefix), ""); } } if (!this.isPackageInstalled(packageName)) { String message = "Package is not installed in the device or invalid package name"; Preference.putString(context, context.getResources().getString(R.string.app_uninstall_status), APP_STATE_UNINSTALLED_FAILED); Preference.putString(context, context.getResources().getString(R.string.app_uninstall_failed_message), message); throw new AndroidAgentException("Package is not installed in the device"); } if (schedule != null && !schedule.trim().isEmpty() && !schedule.equals("undefined")) { try { AlarmUtils.setOneTimeAlarm(context, schedule, Constants.Operation.UNINSTALL_APPLICATION, null, null, packageName); } catch (ParseException e) { Log.e(TAG, "One time alarm time string parsing failed." + e); } return; //Will call uninstallApplication method again upon alarm. } if (Constants.SYSTEM_APP_ENABLED) { Preference.putString(context, context.getResources().getString(R.string.app_uninstall_status), APP_STATE_UNINSTALLED); Preference.putString(context, context.getResources().getString(R.string.app_uninstall_failed_message), null); CommonUtils.callSystemApp(context, Constants.Operation.SILENT_UNINSTALL_APPLICATION, "", packageUriString); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && policyManager.isDeviceOwnerApp(Constants.AGENT_PACKAGE)) { if (silentlyUninstallApplication(packageName)) { Preference.putString(context, context.getResources().getString(R.string.app_uninstall_status), APP_STATE_UNINSTALLED); } else { Preference.putString(context, context.getResources().getString(R.string.app_uninstall_status), APP_STATE_UNINSTALLED_FAILED); } Preference.putString(context, context.getResources().getString(R.string.app_uninstall_failed_message), null); } else { Preference.putString(context, context.getResources().getString(R.string.app_uninstall_status), APP_STATE_UNINSTALLED); Preference.putString(context, context.getResources().getString(R.string.app_uninstall_failed_message), null); Uri packageUri = Uri.parse(packageUriString); Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageUri); uninstallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(uninstallIntent); } } /** * Silently removes an application from the device (agent must be device owner). * * @param packageName - Application package name should be passed in as a String. */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private boolean silentlyUninstallApplication(String packageName) { PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.setAppPackageName(packageName); int sessionId; try { sessionId = packageManager.getPackageInstaller().createSession(params); } catch (IOException e) { return false; } packageManager.getPackageInstaller().uninstall(packageName, PendingIntent .getBroadcast(context, sessionId, new Intent("android.intent.action.MAIN"), 0).getIntentSender()); return true; } /** * Creates a webclip on the device home screen. * * @param url - URL should be passed in as a String. * @param title - Title(Web app title) should be passed in as a String. */ public void manageWebAppBookmark(String url, String title, String operationType) throws AndroidAgentException { final Intent bookmarkIntent = new Intent(); final Intent actionIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); long urlHash = url.hashCode(); long uniqueId = (urlHash << MAX_URL_HASH) | actionIntent.hashCode(); actionIntent.putExtra(Browser.EXTRA_APPLICATION_ID, Long.toString(uniqueId)); bookmarkIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent); bookmarkIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, title); bookmarkIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.drawable.ic_bookmark)); if (operationType != null) { if (resources.getString(R.string.operation_install).equalsIgnoreCase(operationType)) { bookmarkIntent.setAction(resources.getString(R.string.application_package_launcher_install_action)); } else if (resources.getString(R.string.operation_uninstall).equalsIgnoreCase(operationType)) { bookmarkIntent .setAction(resources.getString(R.string.application_package_launcher_uninstall_action)); } else { throw new AndroidAgentException("Cannot create webclip due to invalid operation type."); } } else { bookmarkIntent.setAction(resources.getString(R.string.application_package_launcher_install_action)); } context.sendBroadcastAsUser(bookmarkIntent, android.os.Process.myUserHandle()); } public List<ApplicationInfo> getInstalledApplications() { return packageManager.getInstalledApplications(PackageManager.GET_META_DATA); } public Operation getApplicationInstallationStatus(Operation operation, String status, String message) { switch (status) { case APP_STATE_DOWNLOAD_STARTED: operation.setStatus(context.getResources().getString(R.string.operation_value_progress)); operation.setOperationResponse("Application download started"); break; case APP_STATE_DOWNLOAD_COMPLETED: operation.setStatus(context.getResources().getString(R.string.operation_value_progress)); operation.setOperationResponse("Application download completed"); break; case APP_STATE_DOWNLOAD_FAILED: operation.setStatus(context.getResources().getString(R.string.operation_value_error)); operation.setOperationResponse(message); break; case APP_STATE_INSTALL_FAILED: operation.setStatus(context.getResources().getString(R.string.operation_value_error)); operation.setOperationResponse(message); break; case APP_STATE_INSTALLED: operation.setStatus(context.getResources().getString(R.string.operation_value_completed)); operation.setOperationResponse("Application installation completed"); break; case APP_STATE_UNINSTALLED_FAILED: operation.setStatus(context.getResources().getString(R.string.operation_value_error)); operation.setOperationResponse(message); break; case APP_STATE_UNINSTALLED: operation.setStatus(context.getResources().getString(R.string.operation_value_completed)); operation.setOperationResponse("Application uninstallation completed"); break; default: } return operation; } /** * Checks whether the DownloadManager is available on the device. * * @param context - Context of the calling activity. */ public boolean isDownloadManagerAvailable(Context context) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setClassName(resources.getString(R.string.android_download_manager_ui_resolver), resources.getString(R.string.android_download_manager_list_resolver)); return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY) .size() > 0; } private void removeExistingFile() { String directory = Environment.getExternalStorageDirectory().getPath() + resources.getString(R.string.application_mgr_download_location); File file = new File(directory); file.mkdirs(); File outputFile = new File(file, resources.getString(R.string.application_mgr_download_file_name)); if (outputFile.exists()) { outputFile.delete(); } } /** * Initiate downloading via DownloadManager API. * * @param url - File URL. * @param appName - Name of the application to be downloaded. */ private void downloadViaDownloadManager(String url, String appName) { final DownloadManager downloadManager = (DownloadManager) context .getSystemService(Context.DOWNLOAD_SERVICE); Uri downloadUri = Uri.parse(url); DownloadManager.Request request = new DownloadManager.Request(downloadUri); // Restrict the types of networks over which this download may // proceed. request.setAllowedNetworkTypes( DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE); // Set whether this download may proceed over a roaming connection. request.setAllowedOverRoaming(true); // Set the title of this download, to be displayed in notifications // (if enabled). request.setTitle(resources.getString(R.string.downloader_message_title)); request.setVisibleInDownloadsUi(false); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN); // Set the local destination for the downloaded file to a path // within the application's external files directory request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, appName); // Enqueue a new download and same the referenceId downloadReference = downloadManager.enqueue(request); new Thread(new Runnable() { @Override public void run() { boolean downloading = true; int progress = 0; while (downloading) { downloadOngoing = true; DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadReference); Cursor cursor = downloadManager.query(query); cursor.moveToFirst(); int bytesDownloaded = cursor .getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); int bytesTotal = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); if (cursor.getInt(cursor .getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) { downloading = false; } if (cursor.getInt(cursor .getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_FAILED) { downloading = false; Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources().getString(R.string.app_status_value_download_failed)); Preference.putString(context, context.getResources().getString(R.string.app_install_failed_message), "App download failed due to a connection issue."); } int downloadProgress = 0; if (bytesTotal > 0) { downloadProgress = (int) ((bytesDownloaded * 100l) / bytesTotal); } if (downloadProgress != DOWNLOAD_PERCENTAGE_TOTAL) { progress += DOWNLOADER_INCREMENT; } else { progress = DOWNLOAD_PERCENTAGE_TOTAL; Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources().getString(R.string.app_status_value_download_completed)); } Preference.putString(context, resources.getString(R.string.app_download_progress), String.valueOf(progress)); cursor.close(); } downloadOngoing = false; } }).start(); } /** * Installs or updates an application to the device. * * @param url - APK Url should be passed in as a String. */ private void downloadApp(String url) { RequestQueue queue = null; try { queue = ServerUtilities.getCertifiedHttpClient(); } catch (IDPTokenManagerException e) { Log.e(TAG, "Failed to retrieve HTTP client", e); } InputStreamVolleyRequest request = new InputStreamVolleyRequest(Request.Method.GET, url, new Response.Listener<byte[]>() { @Override public void onResponse(byte[] response) { if (response != null) { FileOutputStream outStream = null; InputStream inStream = null; try { String directory = Environment.getExternalStorageDirectory().getPath() + resources.getString(R.string.application_mgr_download_location); File file = new File(directory); file.mkdirs(); File outputFile = new File(file, resources.getString(R.string.application_mgr_download_file_name)); if (outputFile.exists()) { outputFile.delete(); } outStream = new FileOutputStream(outputFile); inStream = new ByteArrayInputStream(response); byte[] buffer = new byte[BUFFER_SIZE]; int lengthFile; while ((lengthFile = inStream.read(buffer)) != READ_FAILED) { outStream.write(buffer, BUFFER_OFFSET, lengthFile); downloadOngoing = true; } String filePath = directory + resources.getString(R.string.application_mgr_download_file_name); Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources() .getString(R.string.app_status_value_download_completed)); //android 7 and later versions require file URIs from a provider if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", new File(filePath)); triggerInstallation(apkURI); } else { triggerInstallation(Uri.fromFile(new File(filePath))); } } catch (IOException e) { String error = "File download/save failure in AppUpdator."; Log.e(TAG, error, e); Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources() .getString(R.string.app_status_value_download_failed)); Preference.putString(context, context.getResources().getString(R.string.app_install_failed_message), error); } catch (IllegalArgumentException e) { String error = "Error occurred while sending 'Get' request due to empty host name"; Log.e(TAG, error); Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources() .getString(R.string.app_status_value_download_failed)); Preference.putString(context, context.getResources().getString(R.string.app_install_failed_message), error); } finally { StreamHandler.closeOutputStream(outStream, TAG); StreamHandler.closeInputStream(inStream, TAG); downloadOngoing = false; } } else { Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources().getString(R.string.app_status_value_download_failed)); Preference.putString(context, context.getResources().getString(R.string.app_install_failed_message), "File download failed."); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e(TAG, error.toString()); Preference.putString(context, context.getResources().getString(R.string.app_install_status), context.getResources().getString(R.string.app_status_value_download_failed)); Preference.putString(context, context.getResources().getString(R.string.app_install_failed_message), error.toString()); downloadOngoing = false; } }, null) { @Override public Map<String, String> getHeaders() throws AuthFailureError { Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("Accept", "*/*"); headers.put("User-Agent", "Mozilla/5.0 ( compatible ), Android"); return headers; } @Override protected Response<byte[]> parseNetworkResponse(NetworkResponse response) { //Initialise local responseHeaders map with response headers received responseHeaders = response.headers; //Pass the response data here if (response.statusCode == 200) { return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response)); } else { VolleyError error = new VolleyError("Invalid application file URL."); return Response.error(error); } } }; request.setRetryPolicy(new DefaultRetryPolicy(Constants.RetryPolicy.DEFAULT_TIME_OUT, Constants.RetryPolicy.DEFAULT_MAX_RETRIES, Constants.RetryPolicy.DEFAULT_BACKOFF_MULT) { public void retry(VolleyError error) throws VolleyError { Log.w(TAG, "Retrying download the apk... " + getCurrentRetryCount()); super.retry(error); } }); queue.add(request); } }