1/*
2 * Copyright (C) 2011 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
17package com.android.providers.downloads;
18
19import android.content.res.Resources;
20import android.util.Log;
21import android.util.LongSparseArray;
22
23import com.android.internal.annotations.GuardedBy;
24
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.Iterator;
28import java.util.LinkedHashMap;
29
30public class DownloadHandler {
31    private static final String TAG = "DownloadHandler";
32
33    @GuardedBy("this")
34    private final LinkedHashMap<Long, DownloadInfo> mDownloadsQueue =
35            new LinkedHashMap<Long, DownloadInfo>();
36    @GuardedBy("this")
37    private final HashMap<Long, DownloadInfo> mDownloadsInProgress =
38            new HashMap<Long, DownloadInfo>();
39    @GuardedBy("this")
40    private final LongSparseArray<Long> mCurrentSpeed = new LongSparseArray<Long>();
41
42    private final int mMaxConcurrentDownloadsAllowed = Resources.getSystem().getInteger(
43            com.android.internal.R.integer.config_MaxConcurrentDownloadsAllowed);
44
45    private static final DownloadHandler sDownloadHandler = new DownloadHandler();
46
47    public static DownloadHandler getInstance() {
48        return sDownloadHandler;
49    }
50
51    public synchronized void enqueueDownload(DownloadInfo info) {
52        if (!mDownloadsQueue.containsKey(info.mId)) {
53            if (Constants.LOGV) {
54                Log.i(TAG, "enqueued download. id: " + info.mId + ", uri: " + info.mUri);
55            }
56            mDownloadsQueue.put(info.mId, info);
57            startDownloadThreadLocked();
58        }
59    }
60
61    private void startDownloadThreadLocked() {
62        Iterator<Long> keys = mDownloadsQueue.keySet().iterator();
63        ArrayList<Long> ids = new ArrayList<Long>();
64        while (mDownloadsInProgress.size() < mMaxConcurrentDownloadsAllowed && keys.hasNext()) {
65            Long id = keys.next();
66            DownloadInfo info = mDownloadsQueue.get(id);
67            info.startDownloadThread();
68            ids.add(id);
69            mDownloadsInProgress.put(id, mDownloadsQueue.get(id));
70            if (Constants.LOGV) {
71                Log.i(TAG, "started download for : " + id);
72            }
73        }
74        for (Long id : ids) {
75            mDownloadsQueue.remove(id);
76        }
77    }
78
79    public synchronized boolean hasDownloadInQueue(long id) {
80        return mDownloadsQueue.containsKey(id) || mDownloadsInProgress.containsKey(id);
81    }
82
83    public synchronized void dequeueDownload(long id) {
84        mDownloadsInProgress.remove(id);
85        mCurrentSpeed.remove(id);
86        startDownloadThreadLocked();
87        if (mDownloadsInProgress.size() == 0 && mDownloadsQueue.size() == 0) {
88            notifyAll();
89        }
90    }
91
92    public synchronized void setCurrentSpeed(long id, long speed) {
93        mCurrentSpeed.put(id, speed);
94    }
95
96    public synchronized long getCurrentSpeed(long id) {
97        return mCurrentSpeed.get(id, -1L);
98    }
99
100    // right now this is only used by tests. but there is no reason why it can't be used
101    // by any module using DownloadManager (TODO add API to DownloadManager.java)
102    public synchronized void waitUntilDownloadsTerminate() throws InterruptedException {
103        if (mDownloadsInProgress.size() == 0 && mDownloadsQueue.size() == 0) {
104            if (Constants.LOGVV) {
105                Log.i(TAG, "nothing to wait on");
106            }
107            return;
108        }
109        if (Constants.LOGVV) {
110            for (DownloadInfo info : mDownloadsInProgress.values()) {
111                Log.i(TAG, "** progress: " + info.mId + ", " + info.mUri);
112            }
113            for (DownloadInfo info : mDownloadsQueue.values()) {
114                Log.i(TAG, "** in Q: " + info.mId + ", " + info.mUri);
115            }
116        }
117        if (Constants.LOGVV) {
118            Log.i(TAG, "waiting for 5 sec");
119        }
120        // wait upto 5 sec
121        wait(5 * 1000);
122    }
123}
124