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