1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.downloads; 18 19 import static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI; 20 21 import static com.android.providers.downloads.Constants.TAG; 22 23 import android.app.job.JobParameters; 24 import android.app.job.JobService; 25 import android.database.ContentObserver; 26 import android.util.Log; 27 import android.util.SparseArray; 28 29 /** 30 * Service that hosts download jobs. Each active download job is handled as a 31 * unique {@link DownloadThread} instance. 32 * <p> 33 * The majority of downloads should have ETag values to enable resuming, so if a 34 * given download isn't able to finish in the normal job timeout (10 minutes), 35 * we just reschedule the job and resume again in the future. 36 */ 37 public class DownloadJobService extends JobService { 38 // @GuardedBy("mActiveThreads") 39 private SparseArray<DownloadThread> mActiveThreads = new SparseArray<>(); 40 41 @Override onCreate()42 public void onCreate() { 43 super.onCreate(); 44 45 // While someone is bound to us, watch for database changes that should 46 // trigger notification updates. 47 getContentResolver().registerContentObserver(ALL_DOWNLOADS_CONTENT_URI, true, mObserver); 48 } 49 50 @Override onDestroy()51 public void onDestroy() { 52 super.onDestroy(); 53 getContentResolver().unregisterContentObserver(mObserver); 54 } 55 56 @Override onStartJob(JobParameters params)57 public boolean onStartJob(JobParameters params) { 58 final int id = params.getJobId(); 59 60 // Spin up thread to handle this download 61 final DownloadInfo info = DownloadInfo.queryDownloadInfo(this, id); 62 if (info == null) { 63 Log.w(TAG, "Odd, no details found for download " + id); 64 return false; 65 } 66 67 final DownloadThread thread; 68 synchronized (mActiveThreads) { 69 if (mActiveThreads.indexOfKey(id) >= 0) { 70 Log.w(TAG, "Odd, already running download " + id); 71 return false; 72 } 73 thread = new DownloadThread(this, params, info); 74 mActiveThreads.put(id, thread); 75 } 76 thread.start(); 77 78 return true; 79 } 80 81 @Override onStopJob(JobParameters params)82 public boolean onStopJob(JobParameters params) { 83 final int id = params.getJobId(); 84 Log.d(TAG, "onStopJob id=" + id + ", reason=" + params.getStopReason()); 85 86 final DownloadThread thread; 87 synchronized (mActiveThreads) { 88 thread = mActiveThreads.removeReturnOld(id); 89 } 90 if (thread != null) { 91 // If the thread is still running, ask it to gracefully shutdown, 92 // and reschedule ourselves to resume in the future. 93 thread.requestShutdown(); 94 95 Helpers.scheduleJob(this, DownloadInfo.queryDownloadInfo(this, id)); 96 } 97 return false; 98 } 99 jobFinishedInternal(JobParameters params, boolean needsReschedule)100 public void jobFinishedInternal(JobParameters params, boolean needsReschedule) { 101 final int id = params.getJobId(); 102 103 synchronized (mActiveThreads) { 104 mActiveThreads.remove(params.getJobId()); 105 } 106 if (needsReschedule) { 107 Helpers.scheduleJob(this, DownloadInfo.queryDownloadInfo(this, id)); 108 } 109 110 // Update notifications one last time while job is protecting us 111 mObserver.onChange(false); 112 113 // We do our own rescheduling above 114 jobFinished(params, false); 115 } 116 117 private ContentObserver mObserver = new ContentObserver(Helpers.getAsyncHandler()) { 118 @Override 119 public void onChange(boolean selfChange) { 120 Helpers.getDownloadNotifier(DownloadJobService.this).update(); 121 } 122 }; 123 } 124