diff options
Diffstat (limited to 'src/com/android/providers/downloads/DownloadInfo.java')
-rw-r--r-- | src/com/android/providers/downloads/DownloadInfo.java | 260 |
1 files changed, 137 insertions, 123 deletions
diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java index 5172b696..7a912d5a 100644 --- a/src/com/android/providers/downloads/DownloadInfo.java +++ b/src/com/android/providers/downloads/DownloadInfo.java @@ -31,20 +31,26 @@ import android.os.Environment; import android.provider.Downloads; import android.provider.Downloads.Impl; import android.text.TextUtils; -import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; /** * Stores information about an individual download. */ public class DownloadInfo { + // TODO: move towards these in-memory objects being sources of truth, and + // periodically pushing to provider. + public static class Reader { private ContentResolver mResolver; private Cursor mCursor; @@ -54,8 +60,10 @@ public class DownloadInfo { mCursor = cursor; } - public DownloadInfo newDownloadInfo(Context context, SystemFacade systemFacade) { - DownloadInfo info = new DownloadInfo(context, systemFacade); + public DownloadInfo newDownloadInfo(Context context, SystemFacade systemFacade, + StorageManager storageManager, DownloadNotifier notifier) { + final DownloadInfo info = new DownloadInfo( + context, systemFacade, storageManager, notifier); updateFromDatabase(info); readRequestHeaders(info); return info; @@ -71,7 +79,7 @@ public class DownloadInfo { info.mDestination = getInt(Downloads.Impl.COLUMN_DESTINATION); info.mVisibility = getInt(Downloads.Impl.COLUMN_VISIBILITY); info.mStatus = getInt(Downloads.Impl.COLUMN_STATUS); - info.mNumFailed = getInt(Constants.FAILED_CONNECTIONS); + info.mNumFailed = getInt(Downloads.Impl.COLUMN_FAILED_CONNECTIONS); int retryRedirect = getInt(Constants.RETRY_AFTER_X_REDIRECT_COUNT); info.mRetryAfter = retryRedirect & 0xfffffff; info.mLastMod = getLong(Downloads.Impl.COLUMN_LAST_MODIFICATION); @@ -146,44 +154,49 @@ public class DownloadInfo { } } - // the following NETWORK_* constants are used to indicates specfic reasons for disallowing a - // download from using a network, since specific causes can require special handling - - /** - * The network is usable for the given download. - */ - public static final int NETWORK_OK = 1; - - /** - * There is no network connectivity. - */ - public static final int NETWORK_NO_CONNECTION = 2; - - /** - * The download exceeds the maximum size for this network. - */ - public static final int NETWORK_UNUSABLE_DUE_TO_SIZE = 3; - - /** - * The download exceeds the recommended maximum size for this network, the user must confirm for - * this download to proceed without WiFi. - */ - public static final int NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE = 4; - /** - * The current connection is roaming, and the download can't proceed over a roaming connection. + * Constants used to indicate network state for a specific download, after + * applying any requested constraints. */ - public static final int NETWORK_CANNOT_USE_ROAMING = 5; - - /** - * The app requesting the download specific that it can't use the current network connection. - */ - public static final int NETWORK_TYPE_DISALLOWED_BY_REQUESTOR = 6; - - /** - * Current network is blocked for requesting application. - */ - public static final int NETWORK_BLOCKED = 7; + public enum NetworkState { + /** + * The network is usable for the given download. + */ + OK, + + /** + * There is no network connectivity. + */ + NO_CONNECTION, + + /** + * The download exceeds the maximum size for this network. + */ + UNUSABLE_DUE_TO_SIZE, + + /** + * The download exceeds the recommended maximum size for this network, + * the user must confirm for this download to proceed without WiFi. + */ + RECOMMENDED_UNUSABLE_DUE_TO_SIZE, + + /** + * The current connection is roaming, and the download can't proceed + * over a roaming connection. + */ + CANNOT_USE_ROAMING, + + /** + * The app requesting the download specific that it can't use the + * current network connection. + */ + TYPE_DISALLOWED_BY_REQUESTOR, + + /** + * Current network is blocked for requesting application. + */ + BLOCKED; + } /** * For intents used to notify the user that a download exceeds a size threshold, if this extra @@ -191,7 +204,6 @@ public class DownloadInfo { */ public static final String EXTRA_IS_WIFI_REQUIRED = "isWifiRequired"; - public long mId; public String mUri; public boolean mNoIntegrity; @@ -229,12 +241,28 @@ public class DownloadInfo { public int mFuzz; private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>(); - private SystemFacade mSystemFacade; - private Context mContext; - private DownloadInfo(Context context, SystemFacade systemFacade) { + /** + * Result of last {@link DownloadThread} started by + * {@link #startDownloadIfReady(ExecutorService)}. + */ + @GuardedBy("this") + private Future<?> mSubmittedTask; + + @GuardedBy("this") + private DownloadThread mTask; + + private final Context mContext; + private final SystemFacade mSystemFacade; + private final StorageManager mStorageManager; + private final DownloadNotifier mNotifier; + + private DownloadInfo(Context context, SystemFacade systemFacade, StorageManager storageManager, + DownloadNotifier notifier) { mContext = context; mSystemFacade = systemFacade; + mStorageManager = storageManager; + mNotifier = notifier; mFuzz = Helpers.sRandom.nextInt(1001); } @@ -285,14 +313,9 @@ public class DownloadInfo { } /** - * Returns whether this download (which the download manager hasn't seen yet) - * should be started. + * Returns whether this download should be enqueued. */ - private boolean isReadyToStart(long now) { - if (DownloadHandler.getInstance().hasDownloadInQueue(mId)) { - // already running - return false; - } + private boolean isReadyToDownload() { if (mControl == Downloads.Impl.CONTROL_PAUSED) { // the download is paused, so it's not going to start return false; @@ -306,10 +329,11 @@ public class DownloadInfo { case Downloads.Impl.STATUS_WAITING_FOR_NETWORK: case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: - return checkCanUseNetwork() == NETWORK_OK; + return checkCanUseNetwork() == NetworkState.OK; case Downloads.Impl.STATUS_WAITING_TO_RETRY: // download was waiting for a delayed restart + final long now = mSystemFacade.currentTimeMillis(); return restartTime(now) <= now; case Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR: // is the media mounted? @@ -337,21 +361,20 @@ public class DownloadInfo { /** * Returns whether this download is allowed to use the network. - * @return one of the NETWORK_* constants */ - public int checkCanUseNetwork() { + public NetworkState checkCanUseNetwork() { final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mUid); if (info == null || !info.isConnected()) { - return NETWORK_NO_CONNECTION; + return NetworkState.NO_CONNECTION; } if (DetailedState.BLOCKED.equals(info.getDetailedState())) { - return NETWORK_BLOCKED; + return NetworkState.BLOCKED; } - if (!isRoamingAllowed() && mSystemFacade.isNetworkRoaming()) { - return NETWORK_CANNOT_USE_ROAMING; + if (mSystemFacade.isNetworkRoaming() && !isRoamingAllowed()) { + return NetworkState.CANNOT_USE_ROAMING; } - if (!mAllowMetered && mSystemFacade.isActiveNetworkMetered()) { - return NETWORK_TYPE_DISALLOWED_BY_REQUESTOR; + if (mSystemFacade.isActiveNetworkMetered() && !mAllowMetered) { + return NetworkState.TYPE_DISALLOWED_BY_REQUESTOR; } return checkIsNetworkTypeAllowed(info.getType()); } @@ -365,45 +388,16 @@ public class DownloadInfo { } /** - * @return a non-localized string appropriate for logging corresponding to one of the - * NETWORK_* constants. - */ - public String getLogMessageForNetworkError(int networkError) { - switch (networkError) { - case NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE: - return "download size exceeds recommended limit for mobile network"; - - case NETWORK_UNUSABLE_DUE_TO_SIZE: - return "download size exceeds limit for mobile network"; - - case NETWORK_NO_CONNECTION: - return "no network connection available"; - - case NETWORK_CANNOT_USE_ROAMING: - return "download cannot use the current network connection because it is roaming"; - - case NETWORK_TYPE_DISALLOWED_BY_REQUESTOR: - return "download was requested to not use the current network type"; - - case NETWORK_BLOCKED: - return "network is blocked for requesting application"; - - default: - return "unknown error with network connectivity"; - } - } - - /** * Check if this download can proceed over the given network type. * @param networkType a constant from ConnectivityManager.TYPE_*. * @return one of the NETWORK_* constants */ - private int checkIsNetworkTypeAllowed(int networkType) { + private NetworkState checkIsNetworkTypeAllowed(int networkType) { if (mIsPublicApi) { final int flag = translateNetworkTypeToApiFlag(networkType); final boolean allowAllNetworkTypes = mAllowedNetworkTypes == ~0; if (!allowAllNetworkTypes && (flag & mAllowedNetworkTypes) == 0) { - return NETWORK_TYPE_DISALLOWED_BY_REQUESTOR; + return NetworkState.TYPE_DISALLOWED_BY_REQUESTOR; } } return checkSizeAllowedForNetwork(networkType); @@ -433,42 +427,68 @@ public class DownloadInfo { * Check if the download's size prohibits it from running over the current network. * @return one of the NETWORK_* constants */ - private int checkSizeAllowedForNetwork(int networkType) { + private NetworkState checkSizeAllowedForNetwork(int networkType) { if (mTotalBytes <= 0) { - return NETWORK_OK; // we don't know the size yet + return NetworkState.OK; // we don't know the size yet } if (networkType == ConnectivityManager.TYPE_WIFI) { - return NETWORK_OK; // anything goes over wifi + return NetworkState.OK; // anything goes over wifi } Long maxBytesOverMobile = mSystemFacade.getMaxBytesOverMobile(); if (maxBytesOverMobile != null && mTotalBytes > maxBytesOverMobile) { - return NETWORK_UNUSABLE_DUE_TO_SIZE; + return NetworkState.UNUSABLE_DUE_TO_SIZE; } if (mBypassRecommendedSizeLimit == 0) { Long recommendedMaxBytesOverMobile = mSystemFacade.getRecommendedMaxBytesOverMobile(); if (recommendedMaxBytesOverMobile != null && mTotalBytes > recommendedMaxBytesOverMobile) { - return NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE; + return NetworkState.RECOMMENDED_UNUSABLE_DUE_TO_SIZE; } } - return NETWORK_OK; + return NetworkState.OK; } - void startIfReady(long now, StorageManager storageManager) { - if (!isReadyToStart(now)) { - return; - } + /** + * If download is ready to start, and isn't already pending or executing, + * create a {@link DownloadThread} and enqueue it into given + * {@link Executor}. + * + * @return If actively downloading. + */ + public boolean startDownloadIfReady(ExecutorService executor) { + synchronized (this) { + final boolean isReady = isReadyToDownload(); + final boolean isActive = mSubmittedTask != null && !mSubmittedTask.isDone(); + if (isReady && !isActive) { + if (mStatus != Impl.STATUS_RUNNING) { + mStatus = Impl.STATUS_RUNNING; + ContentValues values = new ContentValues(); + values.put(Impl.COLUMN_STATUS, mStatus); + mContext.getContentResolver().update(getAllDownloadsUri(), values, null, null); + } - if (Constants.LOGV) { - Log.v(Constants.TAG, "Service spawning thread to handle download " + mId); + mTask = new DownloadThread( + mContext, mSystemFacade, this, mStorageManager, mNotifier); + mSubmittedTask = executor.submit(mTask); + } + return isReady; } - if (mStatus != Impl.STATUS_RUNNING) { - mStatus = Impl.STATUS_RUNNING; - ContentValues values = new ContentValues(); - values.put(Impl.COLUMN_STATUS, mStatus); - mContext.getContentResolver().update(getAllDownloadsUri(), values, null, null); + } + + /** + * If download is ready to be scanned, enqueue it into the given + * {@link DownloadScanner}. + * + * @return If actively scanning. + */ + public boolean startScanIfReady(DownloadScanner scanner) { + synchronized (this) { + final boolean isReady = shouldScanFile(); + if (isReady) { + scanner.requestScan(this); + } + return isReady; } - DownloadHandler.getInstance().enqueueDownload(this); } public boolean isOnCache() { @@ -529,15 +549,15 @@ public class DownloadInfo { } /** - * Returns the amount of time (as measured from the "now" parameter) - * at which a download will be active. - * 0 = immediately - service should stick around to handle this download. - * -1 = never - service can go away without ever waking up. - * positive value - service must wake up in the future, as specified in ms from "now" + * Return time when this download will be ready for its next action, in + * milliseconds after given time. + * + * @return If {@code 0}, download is ready to proceed immediately. If + * {@link Long#MAX_VALUE}, then download has no future actions. */ - long nextAction(long now) { + public long nextActionMillis(long now) { if (Downloads.Impl.isStatusCompleted(mStatus)) { - return -1; + return Long.MAX_VALUE; } if (mStatus != Downloads.Impl.STATUS_WAITING_TO_RETRY) { return 0; @@ -552,7 +572,7 @@ public class DownloadInfo { /** * Returns whether a file should be scanned */ - boolean shouldScanFile() { + public boolean shouldScanFile() { return (mMediaScanned == 0) && (mDestination == Downloads.Impl.DESTINATION_EXTERNAL || mDestination == Downloads.Impl.DESTINATION_FILE_URI || @@ -570,12 +590,6 @@ public class DownloadInfo { mContext.startActivity(intent); } - void startDownloadThread() { - DownloadThread downloader = new DownloadThread(mContext, mSystemFacade, this, - StorageManager.getInstance(mContext)); - mSystemFacade.startThread(downloader); - } - /** * Query and return status of requested download. */ |