/// <summary> /// Initializes a new instance of the <see cref="DownloadThread"/> class. /// </summary> /// <param name="info"> /// The info. /// </param> /// <param name="service"> /// The service. /// </param> /// <param name="notification"> /// The notification. /// </param> internal DownloadThread(DownloadInfo info, DownloaderService service, DownloadNotification notification) { this.context = service; this.downloadInfo = info; this.downloaderService = service; this.downloadNotification = notification; this.UserAgent = string.Format("APKXDL (Linux; U; Android {0};{1}; {2}/{3}){4}", Build.VERSION.Release, System.Threading.Thread.CurrentThread.CurrentCulture.Name, Build.Device, Build.Id, this.downloaderService.PackageName); }
/// <summary> /// The APK has been updated and a filename has been sent down from the /// Market call. If the file has the same name as the previous file, we do /// nothing as the file is guaranteed to be the same. If the file does not /// have the same name, we download it if it hasn't already been delivered by /// Market. /// </summary> /// <param name="filename"> /// the name of the new file /// </param> /// <param name="fileSize"> /// the size of the new file /// </param> /// <returns> /// The handle file updated. /// </returns> private bool HandleFileUpdated(string filename, long fileSize) { DownloadInfo di = DownloadsDatabase.GetDownloadInfo(filename); if (di != null && di.FileName != null) { if (filename == di.FileName) { return(false); } // remove partially downloaded file if it is there string deleteFile = Helpers.GenerateSaveFileName(this, di.FileName); if (File.Exists(deleteFile)) { File.Delete(deleteFile); } } return(!Helpers.DoesFileExist(this, filename, fileSize, true)); }
/// <summary> /// The allow. /// </summary> /// <param name="reason"> /// The reason. /// </param> /// <exception cref="Java.Lang.RuntimeException"> /// Error with LVL checking and database integrity /// </exception> /// <exception cref="Java.Lang.RuntimeException"> /// Error with getting information from package name /// </exception> public void Allow(PolicyServerResponse reason) { try { int count = this.policy.GetExpansionFilesCount(); if (count == 0) { Debug.WriteLine("No expansion packs."); } DownloadStatus status = 0; for (int index = 0; index < count; index++) { var type = (ApkExpansionPolicy.ExpansionFileType)index; ApkExpansionPolicy.ExpansionFile expansionFile = this.policy.GetExpansionFile(type); string currentFileName = expansionFile.FileName; if (currentFileName != null) { var di = new DownloadInfo { ExpansionFileType = type, FileName = currentFileName, }; if (this.Context.HandleFileUpdated(currentFileName, expansionFile.FileSize)) { status = DownloadStatus.Unknown; di.ResetDownload(); di.Uri = expansionFile.Url; di.TotalBytes = expansionFile.FileSize; di.Status = status; DownloadsDatabase.UpdateDownload(di); } else { // we need to read the download information from the database DownloadInfo dbdi = DownloadsDatabase.GetDownloadInfo(di.FileName); if (dbdi == null) { // the file exists already and is the correct size // was delivered by Market or through another mechanism Debug.WriteLine("file {0} found. Not downloading.", di.FileName); di.Status = DownloadStatus.Success; di.TotalBytes = expansionFile.FileSize; di.CurrentBytes = expansionFile.FileSize; di.Uri = expansionFile.Url; DownloadsDatabase.UpdateDownload(di); } else if (dbdi.Status != DownloadStatus.Success) { // we just update the URL dbdi.Uri = expansionFile.Url; DownloadsDatabase.UpdateDownload(dbdi); status = DownloadStatus.Unknown; } } } } // first: do we need to do an LVL update? // we begin by getting our APK version from the package manager try { PackageInfo pi = this.Context.PackageManager.GetPackageInfo(this.Context.PackageName, 0); DownloadsDatabase.UpdateMetadata(pi.VersionCode, status); DownloadServiceRequirement required = StartDownloadServiceIfRequired( this.Context, this.Context.pPendingIntent, this.Context.GetType()); switch (required) { case DownloadServiceRequirement.NoDownloadRequired: this.Context.downloadNotification.OnDownloadStateChanged(DownloaderState.Completed); break; case DownloadServiceRequirement.LvlCheckRequired: // DANGER WILL ROBINSON! Debug.WriteLine("In LVL checking loop!"); this.Context.downloadNotification.OnDownloadStateChanged( DownloaderState.FailedUnlicensed); throw new RuntimeException("Error with LVL checking and database integrity"); case DownloadServiceRequirement.DownloadRequired: // do nothing: the download will notify the application when things are done break; } } catch (PackageManager.NameNotFoundException e1) { e1.PrintStackTrace(); throw new RuntimeException("Error with getting information from package name"); } catch (Exception ex) { Debug.WriteLine("LVL Update Exception: " + ex.Message); throw; } } catch (Exception ex) { Debug.WriteLine("Allow Exception: " + ex.Message); throw; } finally { this.Context.IsServiceRunning = false; } }
/// <summary> /// The allow. /// </summary> /// <param name="reason"> /// The reason. /// </param> /// <exception cref="Java.Lang.RuntimeException"> /// Error with LVL checking and database integrity /// </exception> /// <exception cref="Java.Lang.RuntimeException"> /// Error with getting information from package name /// </exception> public void Allow(PolicyServerResponse reason) { try { int count = this.policy.GetExpansionFilesCount(); if (count == 0) { Debug.WriteLine("No expansion packs."); } ExpansionDownloadStatus status = 0; for (int index = 0; index < count; index++) { var type = (ApkExpansionPolicy.ExpansionFileType)index; ApkExpansionPolicy.ExpansionFile expansionFile = this.policy.GetExpansionFile(type); string currentFileName = expansionFile.FileName; if (currentFileName != null) { var di = new DownloadInfo { ExpansionFileType = type, FileName = currentFileName, }; if (this.Context.HandleFileUpdated(currentFileName, expansionFile.FileSize)) { status = ExpansionDownloadStatus.Unknown; di.ResetDownload(); di.Uri = expansionFile.Url; di.TotalBytes = expansionFile.FileSize; di.Status = status; DownloadsDatabase.UpdateDownload(di); } else { // we need to read the download information from the database DownloadInfo dbdi = DownloadsDatabase.GetDownloadInfo(di.FileName); if (dbdi == null) { // the file exists already and is the correct size // was delivered by Market or through another mechanism Debug.WriteLine(string.Format("file {0} found. Not downloading.", di.FileName)); di.Status = ExpansionDownloadStatus.Success; di.TotalBytes = expansionFile.FileSize; di.CurrentBytes = expansionFile.FileSize; di.Uri = expansionFile.Url; DownloadsDatabase.UpdateDownload(di); } else if (dbdi.Status != ExpansionDownloadStatus.Success) { // we just update the URL dbdi.Uri = expansionFile.Url; DownloadsDatabase.UpdateDownload(dbdi); status = ExpansionDownloadStatus.Unknown; } } } } // first: do we need to do an LVL update? // we begin by getting our APK version from the package manager try { PackageInfo pi = this.Context.PackageManager.GetPackageInfo(this.Context.PackageName, 0); DownloadsDatabase.UpdateMetadata(pi.VersionCode, status); DownloadServiceRequirement required = StartDownloadServiceIfRequired( this.Context, this.Context.pPendingIntent, this.Context.GetType()); switch (required) { case DownloadServiceRequirement.NoDownloadRequired: this.Context.downloadNotification.OnDownloadStateChanged(DownloaderState.Completed); break; case DownloadServiceRequirement.LvlCheckRequired: // DANGER WILL ROBINSON! Debug.WriteLine("In LVL checking loop!"); this.Context.downloadNotification.OnDownloadStateChanged( DownloaderState.FailedUnlicensed); throw new RuntimeException("Error with LVL checking and database integrity"); case DownloadServiceRequirement.DownloadRequired: // do nothing: the download will notify the application when things are done break; } } catch (PackageManager.NameNotFoundException e1) { e1.PrintStackTrace(); throw new RuntimeException("Error with getting information from package name"); } catch (Exception ex) { Debug.WriteLine("LVL Update Exception: " + ex.Message); throw; } } catch (Exception ex) { Debug.WriteLine("Allow Exception: " + ex.Message); throw; } finally { this.Context.IsServiceRunning = false; } }
/// <summary> /// Initializes a new instance of the <see cref="State"/> class. /// </summary> /// <param name="info"> /// The info. /// </param> /// <param name="service"> /// The service. /// </param> public State(DownloadInfo info, DownloaderService service) { this.RedirectCount = info.RedirectCount; this.RequestUri = info.Uri; this.Filename = service.GenerateTempSaveFileName(info.FileName); }
/// <summary> /// This is the main thread for the Downloader. /// This thread is responsible for queuing up downloads and other goodness. /// </summary> /// <param name="intent"> /// The intent that was recieved. /// </param> protected override void OnHandleIntent(Intent intent) { Log.Debug(Tag, "DownloaderService.OnHandleIntent"); this.IsServiceRunning = true; try { var pendingIntent = (PendingIntent)intent.GetParcelableExtra(DownloaderServiceExtras.PendingIntent); if (null != pendingIntent) { this.downloadNotification.PendingIntent = pendingIntent; this.pPendingIntent = pendingIntent; } else if (null != this.pPendingIntent) { this.downloadNotification.PendingIntent = this.pPendingIntent; } else { Log.Debug(Tag, "LVLDL Downloader started in bad state without notification intent."); return; } // when the LVL check completes, a successful response will update the service if (IsLvlCheckRequired(this.packageInfo)) { this.UpdateLvl(this); return; } // get each download List <DownloadInfo> infos = DownloadsDatabase.GetDownloads(); this.BytesSoFar = 0; this.TotalLength = 0; this.fileCount = infos.Count(); foreach (DownloadInfo info in infos) { // We do an (simple) integrity check on each file, just to // make sure and to verify that the file matches the state if (info.Status == ExpansionDownloadStatus.Success && !Helpers.DoesFileExist(this, info.FileName, info.TotalBytes, true)) { info.Status = ExpansionDownloadStatus.None; info.CurrentBytes = 0; } // get aggregate data this.TotalLength += info.TotalBytes; this.BytesSoFar += info.CurrentBytes; } this.PollNetworkState(); if (this.connectionReceiver == null) { // We use this to track network state, such as when WiFi, Cellular, etc. is enabled // when downloads are paused or in progress. this.connectionReceiver = new InnerBroadcastReceiver(this); var intentFilter = new IntentFilter(ConnectivityManager.ConnectivityAction); intentFilter.AddAction(WifiManager.WifiStateChangedAction); this.RegisterReceiver(this.connectionReceiver, intentFilter); } // loop through all downloads and fetch them int types = Enum.GetValues(typeof(ApkExpansionPolicy.ExpansionFileType)).Length; for (int index = 0; index < types; index++) { DownloadInfo info = infos[index]; Log.Debug(Tag, "Starting download of " + info.FileName); long startingCount = info.CurrentBytes; if (info.Status != ExpansionDownloadStatus.Success) { var dt = new DownloadThread(info, this, this.downloadNotification); this.CancelAlarms(); this.ScheduleAlarm(ActiveThreadWatchdog); dt.Run(); this.CancelAlarms(); } DownloadsDatabase.UpdateFromDatabase(ref info); bool setWakeWatchdog = false; DownloaderState notifyStatus; switch (info.Status) { case ExpansionDownloadStatus.Forbidden: // the URL is out of date this.UpdateLvl(this); return; case ExpansionDownloadStatus.Success: this.BytesSoFar += info.CurrentBytes - startingCount; if (index < infos.Count() - 1) { continue; } DownloadsDatabase.UpdateMetadata(this.packageInfo.VersionCode, ExpansionDownloadStatus.None); this.downloadNotification.OnDownloadStateChanged(DownloaderState.Completed); return; case ExpansionDownloadStatus.FileDeliveredIncorrectly: // we may be on a network that is returning us a web page on redirect notifyStatus = DownloaderState.PausedNetworkSetupFailure; info.CurrentBytes = 0; DownloadsDatabase.UpdateDownload(info); setWakeWatchdog = true; break; case ExpansionDownloadStatus.PausedByApp: notifyStatus = DownloaderState.PausedByRequest; break; case ExpansionDownloadStatus.WaitingForNetwork: case ExpansionDownloadStatus.WaitingToRetry: notifyStatus = DownloaderState.PausedNetworkUnavailable; setWakeWatchdog = true; break; case ExpansionDownloadStatus.QueuedForWifi: case ExpansionDownloadStatus.QueuedForWifiOrCellularPermission: // look for more detail here notifyStatus = this.wifiManager != null && !this.wifiManager.IsWifiEnabled ? DownloaderState.PausedWifiDisabledNeedCellularPermission : DownloaderState.PausedNeedCellularPermission; setWakeWatchdog = true; break; case ExpansionDownloadStatus.Canceled: notifyStatus = DownloaderState.FailedCanceled; setWakeWatchdog = true; break; case ExpansionDownloadStatus.InsufficientSpaceError: notifyStatus = DownloaderState.FailedSdCardFull; setWakeWatchdog = true; break; case ExpansionDownloadStatus.DeviceNotFoundError: notifyStatus = DownloaderState.PausedSdCardUnavailable; setWakeWatchdog = true; break; default: notifyStatus = DownloaderState.Failed; break; } if (setWakeWatchdog) { this.ScheduleAlarm(WatchdogWakeTimer); } else { this.CancelAlarms(); } // failure or pause state this.downloadNotification.OnDownloadStateChanged(notifyStatus); return; } this.downloadNotification.OnDownloadStateChanged(DownloaderState.Completed); } catch (Exception ex) { Log.Error(Tag, ex.Message); Log.Error(Tag, ex.StackTrace); } finally { this.IsServiceRunning = false; } }