Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.download; | 5 package org.chromium.chrome.browser.download; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
| 8 import android.app.DownloadManager; | 8 import android.app.DownloadManager; |
| 9 import android.app.Notification; | 9 import android.app.Notification; |
| 10 import android.app.NotificationManager; | 10 import android.app.NotificationManager; |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 209 return DownloadNotificationService.this; | 209 return DownloadNotificationService.this; |
| 210 } | 210 } |
| 211 } | 211 } |
| 212 | 212 |
| 213 @Override | 213 @Override |
| 214 public void onTaskRemoved(Intent rootIntent) { | 214 public void onTaskRemoved(Intent rootIntent) { |
| 215 super.onTaskRemoved(rootIntent); | 215 super.onTaskRemoved(rootIntent); |
| 216 // If we've lost all Activities, cancel the off the record downloads. | 216 // If we've lost all Activities, cancel the off the record downloads. |
| 217 if (ApplicationStatus.isEveryActivityDestroyed()) { | 217 if (ApplicationStatus.isEveryActivityDestroyed()) { |
| 218 cancelOffTheRecordDownloads(); | 218 cancelOffTheRecordDownloads(); |
| 219 hideSummaryNotificationIfNecessary(); | 219 hideSummaryNotificationIfNecessary(null); |
| 220 } | 220 } |
| 221 } | 221 } |
| 222 | 222 |
| 223 @Override | 223 @Override |
| 224 public void onCreate() { | 224 public void onCreate() { |
| 225 mContext = ContextUtils.getApplicationContext(); | 225 mContext = ContextUtils.getApplicationContext(); |
| 226 mNotificationManager = (NotificationManager) mContext.getSystemService( | 226 mNotificationManager = (NotificationManager) mContext.getSystemService( |
| 227 Context.NOTIFICATION_SERVICE); | 227 Context.NOTIFICATION_SERVICE); |
| 228 mSharedPrefs = ContextUtils.getAppSharedPreferences(); | 228 mSharedPrefs = ContextUtils.getAppSharedPreferences(); |
| 229 mNumAutoResumptionAttemptLeft = mSharedPrefs.getInt(KEY_AUTO_RESUMPTION_ ATTEMPT_LEFT, | 229 mNumAutoResumptionAttemptLeft = mSharedPrefs.getInt(KEY_AUTO_RESUMPTION_ ATTEMPT_LEFT, |
| 230 MAX_RESUMPTION_ATTEMPT_LEFT); | 230 MAX_RESUMPTION_ATTEMPT_LEFT); |
| 231 mDownloadSharedPreferenceHelper = DownloadSharedPreferenceHelper.getInst ance(); | 231 mDownloadSharedPreferenceHelper = DownloadSharedPreferenceHelper.getInst ance(); |
| 232 mNextNotificationId = mSharedPrefs.getInt( | 232 mNextNotificationId = mSharedPrefs.getInt( |
| 233 KEY_NEXT_DOWNLOAD_NOTIFICATION_ID, STARTING_NOTIFICATION_ID); | 233 KEY_NEXT_DOWNLOAD_NOTIFICATION_ID, STARTING_NOTIFICATION_ID); |
| 234 } | 234 } |
| 235 | 235 |
| 236 @Override | 236 @Override |
| 237 public void onDestroy() { | 237 public void onDestroy() { |
| 238 updateNotificationsForShutdown(); | 238 updateNotificationsForShutdown(); |
| 239 rescheduleDownloads(); | 239 rescheduleDownloads(); |
| 240 super.onDestroy(); | 240 super.onDestroy(); |
| 241 } | 241 } |
| 242 | 242 |
| 243 @Override | 243 @Override |
| 244 public int onStartCommand(final Intent intent, int flags, int startId) { | 244 public int onStartCommand(final Intent intent, int flags, int startId) { |
| 245 if (intent == null) { | 245 if (intent == null) { |
| 246 updateNotificationsForShutdown(); | 246 updateNotificationsForShutdown(); |
| 247 handleDownloadOperation( | 247 handleDownloadOperation( |
| 248 new Intent(DownloadNotificationService.ACTION_DOWNLOAD_RESUM E_ALL)); | 248 new Intent(DownloadNotificationService.ACTION_DOWNLOAD_RESUM E_ALL)); |
| 249 hideSummaryNotificationIfNecessary(); | 249 hideSummaryNotificationIfNecessary(null); |
| 250 } else if (isDownloadOperationIntent(intent)) { | 250 } else if (isDownloadOperationIntent(intent)) { |
| 251 handleDownloadOperation(intent); | 251 handleDownloadOperation(intent); |
| 252 DownloadResumptionScheduler.getDownloadResumptionScheduler(mContext) .cancelTask(); | 252 DownloadResumptionScheduler.getDownloadResumptionScheduler(mContext) .cancelTask(); |
| 253 // Limit the number of auto resumption attempts in case Chrome falls into a vicious | 253 // Limit the number of auto resumption attempts in case Chrome falls into a vicious |
| 254 // cycle. | 254 // cycle. |
| 255 if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())) { | 255 if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())) { |
| 256 if (mNumAutoResumptionAttemptLeft > 0) { | 256 if (mNumAutoResumptionAttemptLeft > 0) { |
| 257 mNumAutoResumptionAttemptLeft--; | 257 mNumAutoResumptionAttemptLeft--; |
| 258 updateResumptionAttemptLeft(); | 258 updateResumptionAttemptLeft(); |
| 259 } | 259 } |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 283 public void removeObserver(Observer observer) { | 283 public void removeObserver(Observer observer) { |
| 284 mObservers.removeObserver(observer); | 284 mObservers.removeObserver(observer); |
| 285 } | 285 } |
| 286 | 286 |
| 287 /** | 287 /** |
| 288 * Called when browser is killed. Schedule a resumption task and pause all t he download | 288 * Called when browser is killed. Schedule a resumption task and pause all t he download |
| 289 * notifications. | 289 * notifications. |
| 290 */ | 290 */ |
| 291 @VisibleForTesting | 291 @VisibleForTesting |
| 292 void shutdownService() { | 292 void shutdownService() { |
| 293 // TODO(dtrainor): Post a task to make SURE the summary notification is gone... | |
|
David Trainor- moved to gerrit
2017/03/03 20:11:22
I'll look into fixing this before I land the patch
David Trainor- moved to gerrit
2017/03/04 06:46:01
I believe I fixed this without the posted task.
| |
| 293 stopForeground(); | 294 stopForeground(); |
| 294 for (Observer observer : mObservers) observer.onServiceShutdownRequested (); | 295 for (Observer observer : mObservers) observer.onServiceShutdownRequested (); |
| 295 stopSelf(); | 296 stopSelf(); |
| 296 } | 297 } |
| 297 | 298 |
| 298 private void stopForegroundIfNecessary() { | 299 private void stopForegroundIfNecessary() { |
| 299 if (mDownloadsInProgress.size() == 0) stopForeground(); | 300 if (mDownloadsInProgress.size() == 0) stopForeground(); |
| 300 } | 301 } |
| 301 | 302 |
| 302 @VisibleForTesting | 303 @VisibleForTesting |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 382 * greater or equal to O, stop making this a foreground service. | 383 * greater or equal to O, stop making this a foreground service. |
| 383 * @param downloadGuid The guid of the download that has been paused or canc eled and shouldn't | 384 * @param downloadGuid The guid of the download that has been paused or canc eled and shouldn't |
| 384 * be tracked. | 385 * be tracked. |
| 385 */ | 386 */ |
| 386 private void stopTrackingInProgressDownload(String downloadGuid) { | 387 private void stopTrackingInProgressDownload(String downloadGuid) { |
| 387 mDownloadsInProgress.remove(downloadGuid); | 388 mDownloadsInProgress.remove(downloadGuid); |
| 388 stopForegroundIfNecessary(); | 389 stopForegroundIfNecessary(); |
| 389 } | 390 } |
| 390 | 391 |
| 391 /** | 392 /** |
| 393 * @see #hideSummaryNotificationIfNecessary(Integer) | |
| 394 * @param notificationIdToIgnore If not {@code null}, the id of a notificati on to ignore and | |
| 395 * assume is closing or about to be closed. | |
| 396 * @return Whether or not there are valid download notifications currently v isible. | |
| 397 */ | |
| 398 @VisibleForTesting | |
| 399 @TargetApi(Build.VERSION_CODES.M) | |
| 400 boolean hasDownloadNotifications(Integer notificationIdToIgnore) { | |
| 401 if (!BuildInfo.isAtLeastO()) return false; | |
|
qinmin
2017/03/03 20:48:40
nit: android version doesn't matter with this func
David Trainor- moved to gerrit
2017/03/04 06:46:01
I figured it's a good safety catch in case someone
| |
| 402 | |
| 403 StatusBarNotification[] notifications = mNotificationManager.getActiveNo tifications(); | |
| 404 for (StatusBarNotification notification : notifications) { | |
| 405 boolean isDownloadsGroup = TextUtils.equals(notification.getNotifica tion().getGroup(), | |
| 406 NotificationConstants.GROUP_DOWNLOADS); | |
| 407 boolean isSummaryNotification = | |
| 408 notification.getId() == NotificationConstants.NOTIFICATION_I D_DOWNLOAD_SUMMARY; | |
| 409 boolean isIgnoredNotification = notificationIdToIgnore != null | |
| 410 && notificationIdToIgnore == notification.getId(); | |
| 411 if (isDownloadsGroup && !isSummaryNotification && !isIgnoredNotifica tion) return true; | |
| 412 } | |
| 413 | |
| 414 return false; | |
| 415 } | |
| 416 | |
| 417 @VisibleForTesting | |
| 418 void cancelSummaryNotification() { | |
| 419 mNotificationManager.cancel(NotificationConstants.NOTIFICATION_ID_DOWNLO AD_SUMMARY); | |
| 420 } | |
| 421 | |
| 422 /** | |
| 392 * Check all current notifications and hide the summary notification if we h ave no downloads | 423 * Check all current notifications and hide the summary notification if we h ave no downloads |
| 393 * notifications left. On Android if the user swipes away the last download notification the | 424 * notifications left. On Android if the user swipes away the last download notification the |
| 394 * summary will be dismissed. But if the last downloads notification is dis missed via | 425 * summary will be dismissed. But if the last downloads notification is dis missed via |
| 395 * {@link NotificationManager#cancel(int)}, the summary will remain, so we n eed to check and | 426 * {@link NotificationManager#cancel(int)}, the summary will remain, so we n eed to check and |
| 396 * manually remove it ourselves. | 427 * manually remove it ourselves. |
| 428 * @param notificationIdToIgnore Cancelling a notification and querying for the current list of | |
| 429 * active notifications isn't synchronous. Pa ss a notification id | |
| 430 * here if there is a notification that should be assumed gone. | |
| 431 * Or pass {@code null} if no notification fit s that criteria. | |
| 397 */ | 432 */ |
| 398 @TargetApi(Build.VERSION_CODES.M) | 433 void hideSummaryNotificationIfNecessary(Integer notificationIdToIgnore) { |
| 399 void hideSummaryNotificationIfNecessary() { | |
| 400 if (!BuildInfo.isAtLeastO()) return; | 434 if (!BuildInfo.isAtLeastO()) return; |
| 435 if (mDownloadsInProgress.size() > 0) return; | |
| 401 | 436 |
| 402 stopForegroundIfNecessary(); | 437 stopForegroundIfNecessary(); |
| 403 | 438 if (hasDownloadNotifications(notificationIdToIgnore)) return; |
| 404 StatusBarNotification[] notifications = mNotificationManager.getActiveNo tifications(); | 439 cancelSummaryNotification(); |
| 405 for (StatusBarNotification notification : notifications) { | |
| 406 if (TextUtils.equals(notification.getNotification().getGroup(), | |
| 407 NotificationConstants.GROUP_DOWNLOADS) | |
| 408 && notification.getId() | |
| 409 != NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SU MMARY) { | |
| 410 return; | |
| 411 } | |
| 412 } | |
| 413 mNotificationManager.cancel(NotificationConstants.NOTIFICATION_ID_DOWNLO AD_SUMMARY); | |
| 414 | 440 |
| 415 // Added to shut down the service when everything is gone. | 441 // Added to shut down the service when everything is gone. |
| 416 // TODO(dtrainor): Make sure this makes sense. | |
| 417 shutdownService(); | 442 shutdownService(); |
| 418 } | 443 } |
| 419 | 444 |
| 420 @Override | 445 @Override |
| 421 public IBinder onBind(Intent intent) { | 446 public IBinder onBind(Intent intent) { |
| 422 return mBinder; | 447 return mBinder; |
| 423 } | 448 } |
| 424 | 449 |
| 425 /** | 450 /** |
| 426 * Helper method to update the remaining number of background resumption att empts left. | 451 * Helper method to update the remaining number of background resumption att empts left. |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 565 /** | 590 /** |
| 566 * Cancel a download notification. | 591 * Cancel a download notification. |
| 567 * @param notificationId Notification ID of the download | 592 * @param notificationId Notification ID of the download |
| 568 * @param downloadGuid GUID of the download. | 593 * @param downloadGuid GUID of the download. |
| 569 */ | 594 */ |
| 570 @VisibleForTesting | 595 @VisibleForTesting |
| 571 void cancelNotification(int notificationId, String downloadGuid) { | 596 void cancelNotification(int notificationId, String downloadGuid) { |
| 572 mNotificationManager.cancel(NOTIFICATION_NAMESPACE, notificationId); | 597 mNotificationManager.cancel(NOTIFICATION_NAMESPACE, notificationId); |
| 573 mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(downloadGuid ); | 598 mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(downloadGuid ); |
| 574 stopTrackingInProgressDownload(downloadGuid); | 599 stopTrackingInProgressDownload(downloadGuid); |
| 575 hideSummaryNotificationIfNecessary(); | 600 hideSummaryNotificationIfNecessary(notificationId); |
| 576 } | 601 } |
| 577 | 602 |
| 578 /** | 603 /** |
| 579 * Called when a download is canceled. | 604 * Called when a download is canceled. |
| 580 * @param downloadGuid GUID of the download. | 605 * @param downloadGuid GUID of the download. |
| 581 */ | 606 */ |
| 582 @VisibleForTesting | 607 @VisibleForTesting |
| 583 public void notifyDownloadCanceled(String downloadGuid) { | 608 public void notifyDownloadCanceled(String downloadGuid) { |
| 584 DownloadSharedPreferenceEntry entry = | 609 DownloadSharedPreferenceEntry entry = |
| 585 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry (downloadGuid); | 610 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry (downloadGuid); |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 818 * @param intent Intent with the download operation. | 843 * @param intent Intent with the download operation. |
| 819 */ | 844 */ |
| 820 private void handleDownloadOperation(final Intent intent) { | 845 private void handleDownloadOperation(final Intent intent) { |
| 821 // TODO(qinmin): Figure out how to properly handle this case. | 846 // TODO(qinmin): Figure out how to properly handle this case. |
| 822 boolean isOfflinePage = | 847 boolean isOfflinePage = |
| 823 IntentUtils.safeGetBooleanExtra(intent, EXTRA_IS_OFFLINE_PAGE, f alse); | 848 IntentUtils.safeGetBooleanExtra(intent, EXTRA_IS_OFFLINE_PAGE, f alse); |
| 824 final DownloadSharedPreferenceEntry entry = getDownloadEntryFromIntent(i ntent); | 849 final DownloadSharedPreferenceEntry entry = getDownloadEntryFromIntent(i ntent); |
| 825 if (entry == null | 850 if (entry == null |
| 826 && !(isOfflinePage && TextUtils.equals(intent.getAction(), ACTIO N_DOWNLOAD_OPEN))) { | 851 && !(isOfflinePage && TextUtils.equals(intent.getAction(), ACTIO N_DOWNLOAD_OPEN))) { |
| 827 handleDownloadOperationForMissingNotification(intent); | 852 handleDownloadOperationForMissingNotification(intent); |
| 828 hideSummaryNotificationIfNecessary(); | 853 hideSummaryNotificationIfNecessary(null); |
| 829 return; | 854 return; |
| 830 } | 855 } |
| 831 | 856 |
| 832 if (ACTION_DOWNLOAD_PAUSE.equals(intent.getAction())) { | 857 if (ACTION_DOWNLOAD_PAUSE.equals(intent.getAction())) { |
| 833 // If browser process already goes away, the download should have al ready paused. Do | 858 // If browser process already goes away, the download should have al ready paused. Do |
| 834 // nothing in that case. | 859 // nothing in that case. |
| 835 if (!DownloadManagerService.hasDownloadManagerService()) { | 860 if (!DownloadManagerService.hasDownloadManagerService()) { |
| 836 notifyDownloadPaused(entry.downloadGuid, !entry.isOffTheRecord, false); | 861 notifyDownloadPaused(entry.downloadGuid, !entry.isOffTheRecord, false); |
| 837 hideSummaryNotificationIfNecessary(); | 862 hideSummaryNotificationIfNecessary(null); |
| 838 return; | 863 return; |
| 839 } | 864 } |
| 840 } else if (ACTION_DOWNLOAD_RESUME.equals(intent.getAction())) { | 865 } else if (ACTION_DOWNLOAD_RESUME.equals(intent.getAction())) { |
| 841 // If user manually resumes a download, update the network type if i t | 866 // If user manually resumes a download, update the network type if i t |
| 842 // is not metered previously. | 867 // is not metered previously. |
| 843 boolean canDownloadWhileMetered = entry.canDownloadWhileMetered | 868 boolean canDownloadWhileMetered = entry.canDownloadWhileMetered |
| 844 || DownloadManagerService.isActiveNetworkMetered(mContext); | 869 || DownloadManagerService.isActiveNetworkMetered(mContext); |
| 845 // Update the SharedPreference entry. | 870 // Update the SharedPreference entry. |
| 846 mDownloadSharedPreferenceHelper.addOrReplaceSharedPreferenceEntry( | 871 mDownloadSharedPreferenceHelper.addOrReplaceSharedPreferenceEntry( |
| 847 new DownloadSharedPreferenceEntry(entry.notificationId, entr y.isOffTheRecord, | 872 new DownloadSharedPreferenceEntry(entry.notificationId, entr y.isOffTheRecord, |
| 848 canDownloadWhileMetered, entry.downloadGuid, entry.f ileName, | 873 canDownloadWhileMetered, entry.downloadGuid, entry.f ileName, |
| 849 entry.itemType, true)); | 874 entry.itemType, true)); |
| 850 } else if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction()) | 875 } else if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction()) |
| 851 && (mDownloadSharedPreferenceHelper.getEntries().isEmpty() | 876 && (mDownloadSharedPreferenceHelper.getEntries().isEmpty() |
| 852 || DownloadManagerService.hasDownloadManagerService())) { | 877 || DownloadManagerService.hasDownloadManagerService())) { |
| 853 hideSummaryNotificationIfNecessary(); | 878 hideSummaryNotificationIfNecessary(null); |
| 854 return; | 879 return; |
| 855 } else if (ACTION_DOWNLOAD_OPEN.equals(intent.getAction())) { | 880 } else if (ACTION_DOWNLOAD_OPEN.equals(intent.getAction())) { |
| 856 // TODO(fgorski): Do we even need to do anything special here, befor e we launch Chrome? | 881 // TODO(fgorski): Do we even need to do anything special here, befor e we launch Chrome? |
| 857 } | 882 } |
| 858 | 883 |
| 859 BrowserParts parts = new EmptyBrowserParts() { | 884 BrowserParts parts = new EmptyBrowserParts() { |
| 860 @Override | 885 @Override |
| 861 public boolean shouldStartGpuProcess() { | 886 public boolean shouldStartGpuProcess() { |
| 862 return false; | 887 return false; |
| 863 } | 888 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 895 resumeAllPendingDownloads(); | 920 resumeAllPendingDownloads(); |
| 896 } else if (ACTION_DOWNLOAD_OPEN.equals(intent.getAction())) { | 921 } else if (ACTION_DOWNLOAD_OPEN.equals(intent.getAction())) { |
| 897 OfflinePageDownloadBridge.openDownloadedPage( | 922 OfflinePageDownloadBridge.openDownloadedPage( |
| 898 IntentUtils.safeGetStringExtra(intent, EXTRA_DOW NLOAD_GUID)); | 923 IntentUtils.safeGetStringExtra(intent, EXTRA_DOW NLOAD_GUID)); |
| 899 } else { | 924 } else { |
| 900 Log.e(TAG, "Unrecognized intent action.", intent); | 925 Log.e(TAG, "Unrecognized intent action.", intent); |
| 901 } | 926 } |
| 902 if (!ACTION_DOWNLOAD_OPEN.equals(intent.getAction())) { | 927 if (!ACTION_DOWNLOAD_OPEN.equals(intent.getAction())) { |
| 903 downloadServiceDelegate.destroyServiceDelegate(); | 928 downloadServiceDelegate.destroyServiceDelegate(); |
| 904 } | 929 } |
| 905 hideSummaryNotificationIfNecessary(); | 930 |
| 931 hideSummaryNotificationIfNecessary(ACTION_DOWNLOAD_CANCEL.equals (intent.getAction()) | |
| 932 ? entry.notificationId | |
| 933 : null); | |
| 906 } | 934 } |
| 907 }; | 935 }; |
| 908 try { | 936 try { |
| 909 ChromeBrowserInitializer.getInstance(mContext).handlePreNativeStartu p(parts); | 937 ChromeBrowserInitializer.getInstance(mContext).handlePreNativeStartu p(parts); |
| 910 ChromeBrowserInitializer.getInstance(mContext).handlePostNativeStart up(true, parts); | 938 ChromeBrowserInitializer.getInstance(mContext).handlePostNativeStart up(true, parts); |
| 911 } catch (ProcessInitException e) { | 939 } catch (ProcessInitException e) { |
| 912 Log.e(TAG, "Unable to load native library.", e); | 940 Log.e(TAG, "Unable to load native library.", e); |
| 913 ChromeApplication.reportStartupErrorAndExit(e); | 941 ChromeApplication.reportStartupErrorAndExit(e); |
| 914 } | 942 } |
| 915 } | 943 } |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1085 return context.getString(R.string.remaining_duration_minutes, minute s); | 1113 return context.getString(R.string.remaining_duration_minutes, minute s); |
| 1086 } else if (minutes > 0) { | 1114 } else if (minutes > 0) { |
| 1087 return context.getString(R.string.remaining_duration_one_minute); | 1115 return context.getString(R.string.remaining_duration_one_minute); |
| 1088 } else if (seconds == 1) { | 1116 } else if (seconds == 1) { |
| 1089 return context.getString(R.string.remaining_duration_one_second); | 1117 return context.getString(R.string.remaining_duration_one_second); |
| 1090 } else { | 1118 } else { |
| 1091 return context.getString(R.string.remaining_duration_seconds, second s); | 1119 return context.getString(R.string.remaining_duration_seconds, second s); |
| 1092 } | 1120 } |
| 1093 } | 1121 } |
| 1094 } | 1122 } |
| OLD | NEW |