13a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey/* 23a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Copyright (C) 2016 The Android Open Source Project 33a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * 43a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 53a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * you may not use this file except in compliance with the License. 63a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * You may obtain a copy of the License at 73a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * 83a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 93a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * 103a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Unless required by applicable law or agreed to in writing, software 113a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 123a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * See the License for the specific language governing permissions and 143a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * limitations under the License. 153a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey */ 163a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 173a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeypackage com.android.providers.downloads; 183a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 193a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI; 203a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 213a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport static com.android.providers.downloads.Constants.TAG; 223a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 233a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.app.job.JobParameters; 243a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.app.job.JobService; 253a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.database.ContentObserver; 263a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.util.Log; 273a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.util.SparseArray; 283a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 293a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey/** 303a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Service that hosts download jobs. Each active download job is handled as a 313a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * unique {@link DownloadThread} instance. 323a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * <p> 333a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * The majority of downloads should have ETag values to enable resuming, so if a 343a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * given download isn't able to finish in the normal job timeout (10 minutes), 353a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * we just reschedule the job and resume again in the future. 363a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey */ 373a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeypublic class DownloadJobService extends JobService { 383a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey // @GuardedBy("mActiveThreads") 393a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private SparseArray<DownloadThread> mActiveThreads = new SparseArray<>(); 403a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 413a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey @Override 423a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey public void onCreate() { 433a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey super.onCreate(); 443a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 453a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey // While someone is bound to us, watch for database changes that should 463a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey // trigger notification updates. 473a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey getContentResolver().registerContentObserver(ALL_DOWNLOADS_CONTENT_URI, true, mObserver); 483a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 493a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 503a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey @Override 513a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey public void onDestroy() { 523a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey super.onDestroy(); 533a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey getContentResolver().unregisterContentObserver(mObserver); 543a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 553a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 563a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey @Override 573a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey public boolean onStartJob(JobParameters params) { 583a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int id = params.getJobId(); 593a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 603a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey // Spin up thread to handle this download 613a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final DownloadInfo info = DownloadInfo.queryDownloadInfo(this, id); 623a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (info == null) { 633a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Log.w(TAG, "Odd, no details found for download " + id); 643a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return false; 653a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 663a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 673a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final DownloadThread thread; 683a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey synchronized (mActiveThreads) { 693a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey thread = new DownloadThread(this, params, info); 703a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey mActiveThreads.put(id, thread); 713a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 723a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey thread.start(); 733a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 743a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return true; 753a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 763a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 773a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey @Override 783a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey public boolean onStopJob(JobParameters params) { 793a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int id = params.getJobId(); 803a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 813a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final DownloadThread thread; 823a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey synchronized (mActiveThreads) { 833a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey thread = mActiveThreads.removeReturnOld(id); 843a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 853a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (thread != null) { 863a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey // If the thread is still running, ask it to gracefully shutdown, 873a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey // and reschedule ourselves to resume in the future. 883a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey thread.requestShutdown(); 893a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 903a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Helpers.scheduleJob(this, DownloadInfo.queryDownloadInfo(this, id)); 913a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 923a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return false; 933a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 943a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 953a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey public void jobFinishedInternal(JobParameters params, boolean needsReschedule) { 963a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey synchronized (mActiveThreads) { 973a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey mActiveThreads.remove(params.getJobId()); 983a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 993a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 1003a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey // Update notifications one last time while job is protecting us 1013a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey mObserver.onChange(false); 1023a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 1033a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey jobFinished(params, needsReschedule); 1043a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 1053a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 1063a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private ContentObserver mObserver = new ContentObserver(Helpers.getAsyncHandler()) { 1073a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey @Override 1083a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey public void onChange(boolean selfChange) { 1093a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Helpers.getDownloadNotifier(DownloadJobService.this).update(); 1103a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 1113a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey }; 1123a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey} 113