summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers/downloads/DownloadService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/providers/downloads/DownloadService.java')
-rw-r--r--src/com/android/providers/downloads/DownloadService.java879
1 files changed, 0 insertions, 879 deletions
diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java
deleted file mode 100644
index 0600cfb6..00000000
--- a/src/com/android/providers/downloads/DownloadService.java
+++ /dev/null
@@ -1,879 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.downloads;
-
-import com.google.android.collect.Lists;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.ServiceConnection;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.CharArrayBuffer;
-import android.drm.mobile1.DrmRawContent;
-import android.media.IMediaScannerService;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Process;
-import android.provider.Downloads;
-import android.util.Config;
-import android.util.Log;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-
-
-/**
- * Performs the background downloads requested by applications that use the Downloads provider.
- */
-public class DownloadService extends Service {
-
- /* ------------ Constants ------------ */
-
- /* ------------ Members ------------ */
-
- /** Observer to get notified when the content observer's data changes */
- private DownloadManagerContentObserver mObserver;
-
- /** Class to handle Notification Manager updates */
- private DownloadNotification mNotifier;
-
- /**
- * The Service's view of the list of downloads. This is kept independently
- * from the content provider, and the Service only initiates downloads
- * based on this data, so that it can deal with situation where the data
- * in the content provider changes or disappears.
- */
- private ArrayList<DownloadInfo> mDownloads;
-
- /**
- * The thread that updates the internal download list from the content
- * provider.
- */
- private UpdateThread updateThread;
-
- /**
- * Whether the internal download list should be updated from the content
- * provider.
- */
- private boolean pendingUpdate;
-
- /**
- * The ServiceConnection object that tells us when we're connected to and disconnected from
- * the Media Scanner
- */
- private MediaScannerConnection mMediaScannerConnection;
-
- private boolean mMediaScannerConnecting;
-
- /**
- * The IPC interface to the Media Scanner
- */
- private IMediaScannerService mMediaScannerService;
-
- /**
- * Array used when extracting strings from content provider
- */
- private CharArrayBuffer oldChars;
-
- /**
- * Array used when extracting strings from content provider
- */
- private CharArrayBuffer newChars;
-
- /* ------------ Inner Classes ------------ */
-
- /**
- * Receives notifications when the data in the content provider changes
- */
- private class DownloadManagerContentObserver extends ContentObserver {
-
- public DownloadManagerContentObserver() {
- super(new Handler());
- }
-
- /**
- * Receives notification when the data in the observed content
- * provider changes.
- */
- public void onChange(final boolean selfChange) {
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service ContentObserver received notification");
- }
- updateFromProvider();
- }
-
- }
-
- /**
- * Gets called back when the connection to the media
- * scanner is established or lost.
- */
- public class MediaScannerConnection implements ServiceConnection {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Connected to Media Scanner");
- }
- mMediaScannerConnecting = false;
- synchronized (DownloadService.this) {
- mMediaScannerService = IMediaScannerService.Stub.asInterface(service);
- if (mMediaScannerService != null) {
- updateFromProvider();
- }
- }
- }
-
- public void disconnectMediaScanner() {
- synchronized (DownloadService.this) {
- if (mMediaScannerService != null) {
- mMediaScannerService = null;
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Disconnecting from Media Scanner");
- }
- try {
- unbindService(this);
- } catch (IllegalArgumentException ex) {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "unbindService threw up: " + ex);
- }
- }
- }
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Disconnected from Media Scanner");
- }
- synchronized (DownloadService.this) {
- mMediaScannerService = null;
- }
- }
- }
-
- /* ------------ Methods ------------ */
-
- /**
- * Returns an IBinder instance when someone wants to connect to this
- * service. Binding to this service is not allowed.
- *
- * @throws UnsupportedOperationException
- */
- public IBinder onBind(Intent i) {
- throw new UnsupportedOperationException("Cannot bind to Download Manager Service");
- }
-
- /**
- * Initializes the service when it is first created
- */
- public void onCreate() {
- super.onCreate();
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service onCreate");
- }
-
- mDownloads = Lists.newArrayList();
-
- mObserver = new DownloadManagerContentObserver();
- getContentResolver().registerContentObserver(Downloads.CONTENT_URI,
- true, mObserver);
-
- mMediaScannerService = null;
- mMediaScannerConnecting = false;
- mMediaScannerConnection = new MediaScannerConnection();
-
- mNotifier = new DownloadNotification(this);
- mNotifier.mNotificationMgr.cancelAll();
- mNotifier.updateNotification();
-
- trimDatabase();
- removeSpuriousFiles();
- updateFromProvider();
- }
-
- /**
- * Responds to a call to startService
- */
- public void onStart(Intent intent, int startId) {
- super.onStart(intent, startId);
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service onStart");
- }
-
- updateFromProvider();
- }
-
- /**
- * Cleans up when the service is destroyed
- */
- public void onDestroy() {
- getContentResolver().unregisterContentObserver(mObserver);
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service onDestroy");
- }
- super.onDestroy();
- }
-
- /**
- * Parses data from the content provider into private array
- */
- private void updateFromProvider() {
- synchronized (this) {
- pendingUpdate = true;
- if (updateThread == null) {
- updateThread = new UpdateThread();
- updateThread.start();
- }
- }
- }
-
- private class UpdateThread extends Thread {
- public UpdateThread() {
- super("Download Service");
- }
-
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
- boolean keepService = false;
- // for each update from the database, remember which download is
- // supposed to get restarted soonest in the future
- long wakeUp = Long.MAX_VALUE;
- for (;;) {
- synchronized (DownloadService.this) {
- if (updateThread != this) {
- throw new IllegalStateException(
- "multiple UpdateThreads in DownloadService");
- }
- if (!pendingUpdate) {
- updateThread = null;
- if (!keepService) {
- stopSelf();
- }
- if (wakeUp != Long.MAX_VALUE) {
- AlarmManager alarms =
- (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- if (alarms == null) {
- Log.e(Constants.TAG, "couldn't get alarm manager");
- } else {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "scheduling retry in " + wakeUp + "ms");
- }
- Intent intent = new Intent(Constants.ACTION_RETRY);
- intent.setClassName("com.android.providers.downloads",
- DownloadReceiver.class.getName());
- alarms.set(
- AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + wakeUp,
- PendingIntent.getBroadcast(DownloadService.this, 0, intent,
- PendingIntent.FLAG_ONE_SHOT));
- }
- }
- oldChars = null;
- newChars = null;
- return;
- }
- pendingUpdate = false;
- }
- boolean networkAvailable = Helpers.isNetworkAvailable(DownloadService.this);
- boolean networkRoaming = Helpers.isNetworkRoaming(DownloadService.this);
- long now = System.currentTimeMillis();
-
- Cursor cursor = getContentResolver().query(Downloads.CONTENT_URI,
- null, null, null, Downloads._ID);
-
- if (cursor == null) {
- return;
- }
-
- cursor.moveToFirst();
-
- int arrayPos = 0;
-
- boolean mustScan = false;
- keepService = false;
- wakeUp = Long.MAX_VALUE;
-
- boolean isAfterLast = cursor.isAfterLast();
-
- int idColumn = cursor.getColumnIndexOrThrow(Downloads._ID);
-
- /*
- * Walk the cursor and the local array to keep them in sync. The key
- * to the algorithm is that the ids are unique and sorted both in
- * the cursor and in the array, so that they can be processed in
- * order in both sources at the same time: at each step, both
- * sources point to the lowest id that hasn't been processed from
- * that source, and the algorithm processes the lowest id from
- * those two possibilities.
- * At each step:
- * -If the array contains an entry that's not in the cursor, remove the
- * entry, move to next entry in the array.
- * -If the array contains an entry that's in the cursor, nothing to do,
- * move to next cursor row and next array entry.
- * -If the cursor contains an entry that's not in the array, insert
- * a new entry in the array, move to next cursor row and next
- * array entry.
- */
- while (!isAfterLast || arrayPos < mDownloads.size()) {
- if (isAfterLast) {
- // We're beyond the end of the cursor but there's still some
- // stuff in the local array, which can only be junk
- if (Constants.LOGVV) {
- int arrayId = ((DownloadInfo) mDownloads.get(arrayPos)).id;
- Log.v(Constants.TAG, "Array update: trimming " +
- arrayId + " @ " + arrayPos);
- }
- if (shouldScanFile(arrayPos) && mediaScannerConnected()) {
- scanFile(null, arrayPos);
- }
- deleteDownload(arrayPos); // this advances in the array
- } else {
- int id = cursor.getInt(idColumn);
-
- if (arrayPos == mDownloads.size()) {
- insertDownload(cursor, arrayPos, networkAvailable, networkRoaming, now);
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Array update: inserting " +
- id + " @ " + arrayPos);
- }
- if (shouldScanFile(arrayPos)
- && (!mediaScannerConnected() || !scanFile(cursor, arrayPos))) {
- mustScan = true;
- keepService = true;
- }
- if (visibleNotification(arrayPos)) {
- keepService = true;
- }
- long next = nextAction(arrayPos, now);
- if (next == 0) {
- keepService = true;
- } else if (next > 0 && next < wakeUp) {
- wakeUp = next;
- }
- ++arrayPos;
- cursor.moveToNext();
- isAfterLast = cursor.isAfterLast();
- } else {
- int arrayId = mDownloads.get(arrayPos).id;
-
- if (arrayId < id) {
- // The array entry isn't in the cursor
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Array update: removing " + arrayId
- + " @ " + arrayPos);
- }
- if (shouldScanFile(arrayPos) && mediaScannerConnected()) {
- scanFile(null, arrayPos);
- }
- deleteDownload(arrayPos); // this advances in the array
- } else if (arrayId == id) {
- // This cursor row already exists in the stored array
- updateDownload(
- cursor, arrayPos,
- networkAvailable, networkRoaming, now);
- if (shouldScanFile(arrayPos)
- && (!mediaScannerConnected()
- || !scanFile(cursor, arrayPos))) {
- mustScan = true;
- keepService = true;
- }
- if (visibleNotification(arrayPos)) {
- keepService = true;
- }
- long next = nextAction(arrayPos, now);
- if (next == 0) {
- keepService = true;
- } else if (next > 0 && next < wakeUp) {
- wakeUp = next;
- }
- ++arrayPos;
- cursor.moveToNext();
- isAfterLast = cursor.isAfterLast();
- } else {
- // This cursor entry didn't exist in the stored array
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Array update: appending " +
- id + " @ " + arrayPos);
- }
- insertDownload(
- cursor, arrayPos,
- networkAvailable, networkRoaming, now);
- if (shouldScanFile(arrayPos)
- && (!mediaScannerConnected()
- || !scanFile(cursor, arrayPos))) {
- mustScan = true;
- keepService = true;
- }
- if (visibleNotification(arrayPos)) {
- keepService = true;
- }
- long next = nextAction(arrayPos, now);
- if (next == 0) {
- keepService = true;
- } else if (next > 0 && next < wakeUp) {
- wakeUp = next;
- }
- ++arrayPos;
- cursor.moveToNext();
- isAfterLast = cursor.isAfterLast();
- }
- }
- }
- }
-
- mNotifier.updateNotification();
-
- if (mustScan) {
- if (!mMediaScannerConnecting) {
- Intent intent = new Intent();
- intent.setClassName("com.android.providers.media",
- "com.android.providers.media.MediaScannerService");
- mMediaScannerConnecting = true;
- bindService(intent, mMediaScannerConnection, BIND_AUTO_CREATE);
- }
- } else {
- mMediaScannerConnection.disconnectMediaScanner();
- }
-
- cursor.close();
- }
- }
- }
-
- /**
- * Removes files that may have been left behind in the cache directory
- */
- private void removeSpuriousFiles() {
- File[] files = Environment.getDownloadCacheDirectory().listFiles();
- if (files == null) {
- // The cache folder doesn't appear to exist (this is likely the case
- // when running the simulator).
- return;
- }
- HashSet<String> fileSet = new HashSet();
- for (int i = 0; i < files.length; i++) {
- if (files[i].getName().equals(Constants.KNOWN_SPURIOUS_FILENAME)) {
- continue;
- }
- if (files[i].getName().equalsIgnoreCase(Constants.RECOVERY_DIRECTORY)) {
- continue;
- }
- fileSet.add(files[i].getPath());
- }
-
- Cursor cursor = getContentResolver().query(Downloads.CONTENT_URI,
- new String[] { Downloads._DATA }, null, null, null);
- if (cursor != null) {
- if (cursor.moveToFirst()) {
- do {
- fileSet.remove(cursor.getString(0));
- } while (cursor.moveToNext());
- }
- cursor.close();
- }
- Iterator<String> iterator = fileSet.iterator();
- while (iterator.hasNext()) {
- String filename = iterator.next();
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "deleting spurious file " + filename);
- }
- new File(filename).delete();
- }
- }
-
- /**
- * Drops old rows from the database to prevent it from growing too large
- */
- private void trimDatabase() {
- Cursor cursor = getContentResolver().query(Downloads.CONTENT_URI,
- new String[] { Downloads._ID },
- Downloads.STATUS + " >= '200'", null,
- Downloads.LAST_MODIFICATION);
- if (cursor == null) {
- // This isn't good - if we can't do basic queries in our database, nothing's gonna work
- Log.e(Constants.TAG, "null cursor in trimDatabase");
- return;
- }
- if (cursor.moveToFirst()) {
- int numDelete = cursor.getCount() - Constants.MAX_DOWNLOADS;
- int columnId = cursor.getColumnIndexOrThrow(Downloads._ID);
- while (numDelete > 0) {
- getContentResolver().delete(
- ContentUris.withAppendedId(Downloads.CONTENT_URI, cursor.getLong(columnId)),
- null, null);
- if (!cursor.moveToNext()) {
- break;
- }
- numDelete--;
- }
- }
- cursor.close();
- }
-
- /**
- * Keeps a local copy of the info about a download, and initiates the
- * download if appropriate.
- */
- private void insertDownload(
- Cursor cursor, int arrayPos,
- boolean networkAvailable, boolean networkRoaming, long now) {
- int statusColumn = cursor.getColumnIndexOrThrow(Downloads.STATUS);
- int failedColumn = cursor.getColumnIndexOrThrow(Constants.FAILED_CONNECTIONS);
- int retryRedirect =
- cursor.getInt(cursor.getColumnIndexOrThrow(Constants.RETRY_AFTER___REDIRECT_COUNT));
- DownloadInfo info = new DownloadInfo(
- cursor.getInt(cursor.getColumnIndexOrThrow(Downloads._ID)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.URI)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.NO_INTEGRITY)) == 1,
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.FILENAME_HINT)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads._DATA)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.MIMETYPE)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.DESTINATION)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.VISIBILITY)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.CONTROL)),
- cursor.getInt(statusColumn),
- cursor.getInt(failedColumn),
- retryRedirect & 0xfffffff,
- retryRedirect >> 28,
- cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.LAST_MODIFICATION)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.NOTIFICATION_PACKAGE)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.NOTIFICATION_CLASS)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.NOTIFICATION_EXTRAS)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.COOKIE_DATA)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.USER_AGENT)),
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.REFERER)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.TOTAL_BYTES)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.CURRENT_BYTES)),
- cursor.getString(cursor.getColumnIndexOrThrow(Constants.ETAG)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Constants.MEDIA_SCANNED)) == 1);
-
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service adding new entry");
- Log.v(Constants.TAG, "ID : " + info.id);
- Log.v(Constants.TAG, "URI : " + ((info.uri != null) ? "yes" : "no"));
- Log.v(Constants.TAG, "NO_INTEG: " + info.noIntegrity);
- Log.v(Constants.TAG, "HINT : " + info.hint);
- Log.v(Constants.TAG, "FILENAME: " + info.filename);
- Log.v(Constants.TAG, "MIMETYPE: " + info.mimetype);
- Log.v(Constants.TAG, "DESTINAT: " + info.destination);
- Log.v(Constants.TAG, "VISIBILI: " + info.visibility);
- Log.v(Constants.TAG, "CONTROL : " + info.control);
- Log.v(Constants.TAG, "STATUS : " + info.status);
- Log.v(Constants.TAG, "FAILED_C: " + info.numFailed);
- Log.v(Constants.TAG, "RETRY_AF: " + info.retryAfter);
- Log.v(Constants.TAG, "REDIRECT: " + info.redirectCount);
- Log.v(Constants.TAG, "LAST_MOD: " + info.lastMod);
- Log.v(Constants.TAG, "PACKAGE : " + info.pckg);
- Log.v(Constants.TAG, "CLASS : " + info.clazz);
- Log.v(Constants.TAG, "COOKIES : " + ((info.cookies != null) ? "yes" : "no"));
- Log.v(Constants.TAG, "AGENT : " + info.userAgent);
- Log.v(Constants.TAG, "REFERER : " + ((info.referer != null) ? "yes" : "no"));
- Log.v(Constants.TAG, "TOTAL : " + info.totalBytes);
- Log.v(Constants.TAG, "CURRENT : " + info.currentBytes);
- Log.v(Constants.TAG, "ETAG : " + info.etag);
- Log.v(Constants.TAG, "SCANNED : " + info.mediaScanned);
- }
-
- mDownloads.add(arrayPos, info);
-
- if (info.status == 0
- && (info.destination == Downloads.DESTINATION_EXTERNAL
- || info.destination == Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE)
- && info.mimetype != null
- && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(info.mimetype)) {
- // Check to see if we are allowed to download this file. Only files
- // that can be handled by the platform can be downloaded.
- // special case DRM files, which we should always allow downloading.
- Intent mimetypeIntent = new Intent(Intent.ACTION_VIEW);
-
- // We can provide data as either content: or file: URIs,
- // so allow both. (I think it would be nice if we just did
- // everything as content: URIs)
- // Actually, right now the download manager's UId restrictions
- // prevent use from using content: so it's got to be file: or
- // nothing
-
- mimetypeIntent.setDataAndType(Uri.fromParts("file", "", null), info.mimetype);
- ResolveInfo ri = getPackageManager().resolveActivity(mimetypeIntent,
- PackageManager.MATCH_DEFAULT_ONLY);
- //Log.i(Constants.TAG, "*** QUERY " + mimetypeIntent + ": " + list);
-
- if (ri == null) {
- if (Config.LOGD) {
- Log.d(Constants.TAG, "no application to handle MIME type " + info.mimetype);
- }
- info.status = Downloads.STATUS_NOT_ACCEPTABLE;
-
- Uri uri = ContentUris.withAppendedId(Downloads.CONTENT_URI, info.id);
- ContentValues values = new ContentValues();
- values.put(Downloads.STATUS, Downloads.STATUS_NOT_ACCEPTABLE);
- getContentResolver().update(uri, values, null, null);
- info.sendIntentIfRequested(uri, this);
- return;
- }
- }
-
- if (info.canUseNetwork(networkAvailable, networkRoaming)) {
- if (info.isReadyToStart(now)) {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "Service spawning thread to handle new download " +
- info.id);
- }
- if (info.hasActiveThread) {
- throw new IllegalStateException("Multiple threads on same download on insert");
- }
- if (info.status != Downloads.STATUS_RUNNING) {
- info.status = Downloads.STATUS_RUNNING;
- ContentValues values = new ContentValues();
- values.put(Downloads.STATUS, info.status);
- getContentResolver().update(
- ContentUris.withAppendedId(Downloads.CONTENT_URI, info.id),
- values, null, null);
- }
- DownloadThread downloader = new DownloadThread(this, info);
- info.hasActiveThread = true;
- downloader.start();
- }
- } else {
- if (info.status == 0
- || info.status == Downloads.STATUS_PENDING
- || info.status == Downloads.STATUS_RUNNING) {
- info.status = Downloads.STATUS_RUNNING_PAUSED;
- Uri uri = ContentUris.withAppendedId(Downloads.CONTENT_URI, info.id);
- ContentValues values = new ContentValues();
- values.put(Downloads.STATUS, Downloads.STATUS_RUNNING_PAUSED);
- getContentResolver().update(uri, values, null, null);
- }
- }
- }
-
- /**
- * Updates the local copy of the info about a download.
- */
- private void updateDownload(
- Cursor cursor, int arrayPos,
- boolean networkAvailable, boolean networkRoaming, long now) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- int statusColumn = cursor.getColumnIndexOrThrow(Downloads.STATUS);
- int failedColumn = cursor.getColumnIndexOrThrow(Constants.FAILED_CONNECTIONS);
- info.id = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads._ID));
- info.uri = stringFromCursor(info.uri, cursor, Downloads.URI);
- info.noIntegrity =
- cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.NO_INTEGRITY)) == 1;
- info.hint = stringFromCursor(info.hint, cursor, Downloads.FILENAME_HINT);
- info.filename = stringFromCursor(info.filename, cursor, Downloads._DATA);
- info.mimetype = stringFromCursor(info.mimetype, cursor, Downloads.MIMETYPE);
- info.destination = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.DESTINATION));
- int newVisibility = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.VISIBILITY));
- if (info.visibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
- && newVisibility != Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
- && Downloads.isStatusCompleted(info.status)) {
- mNotifier.mNotificationMgr.cancel(info.id);
- }
- info.visibility = newVisibility;
- synchronized(info) {
- info.control = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.CONTROL));
- }
- int newStatus = cursor.getInt(statusColumn);
- if (!Downloads.isStatusCompleted(info.status) && Downloads.isStatusCompleted(newStatus)) {
- mNotifier.mNotificationMgr.cancel(info.id);
- }
- info.status = newStatus;
- info.numFailed = cursor.getInt(failedColumn);
- int retryRedirect =
- cursor.getInt(cursor.getColumnIndexOrThrow(Constants.RETRY_AFTER___REDIRECT_COUNT));
- info.retryAfter = retryRedirect & 0xfffffff;
- info.redirectCount = retryRedirect >> 28;
- info.lastMod = cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.LAST_MODIFICATION));
- info.pckg = stringFromCursor(info.pckg, cursor, Downloads.NOTIFICATION_PACKAGE);
- info.clazz = stringFromCursor(info.clazz, cursor, Downloads.NOTIFICATION_CLASS);
- info.cookies = stringFromCursor(info.cookies, cursor, Downloads.COOKIE_DATA);
- info.userAgent = stringFromCursor(info.userAgent, cursor, Downloads.USER_AGENT);
- info.referer = stringFromCursor(info.referer, cursor, Downloads.REFERER);
- info.totalBytes = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.TOTAL_BYTES));
- info.currentBytes = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.CURRENT_BYTES));
- info.etag = stringFromCursor(info.etag, cursor, Constants.ETAG);
- info.mediaScanned =
- cursor.getInt(cursor.getColumnIndexOrThrow(Constants.MEDIA_SCANNED)) == 1;
-
- if (info.canUseNetwork(networkAvailable, networkRoaming)) {
- if (info.isReadyToRestart(now)) {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "Service spawning thread to handle updated download " +
- info.id);
- }
- if (info.hasActiveThread) {
- throw new IllegalStateException("Multiple threads on same download on update");
- }
- info.status = Downloads.STATUS_RUNNING;
- ContentValues values = new ContentValues();
- values.put(Downloads.STATUS, info.status);
- getContentResolver().update(
- ContentUris.withAppendedId(Downloads.CONTENT_URI, info.id),
- values, null, null);
- DownloadThread downloader = new DownloadThread(this, info);
- info.hasActiveThread = true;
- downloader.start();
- }
- }
- }
-
- /**
- * Returns a String that holds the current value of the column,
- * optimizing for the case where the value hasn't changed.
- */
- private String stringFromCursor(String old, Cursor cursor, String column) {
- int index = cursor.getColumnIndexOrThrow(column);
- if (old == null) {
- return cursor.getString(index);
- }
- if (newChars == null) {
- newChars = new CharArrayBuffer(128);
- }
- cursor.copyStringToBuffer(index, newChars);
- int length = newChars.sizeCopied;
- if (length != old.length()) {
- return cursor.getString(index);
- }
- if (oldChars == null || oldChars.sizeCopied < length) {
- oldChars = new CharArrayBuffer(length);
- }
- char[] oldArray = oldChars.data;
- char[] newArray = newChars.data;
- old.getChars(0, length, oldArray, 0);
- for (int i = length - 1; i >= 0; --i) {
- if (oldArray[i] != newArray[i]) {
- return new String(newArray, 0, length);
- }
- }
- return old;
- }
-
- /**
- * Removes the local copy of the info about a download.
- */
- private void deleteDownload(int arrayPos) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- if (info.status == Downloads.STATUS_RUNNING) {
- info.status = Downloads.STATUS_CANCELED;
- } else if (info.destination != Downloads.DESTINATION_EXTERNAL && info.filename != null) {
- new File(info.filename).delete();
- }
- mNotifier.mNotificationMgr.cancel(info.id);
-
- mDownloads.remove(arrayPos);
- }
-
- /**
- * 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"
- */
- private long nextAction(int arrayPos, long now) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- if (Downloads.isStatusCompleted(info.status)) {
- return -1;
- }
- if (info.status != Downloads.STATUS_RUNNING_PAUSED) {
- return 0;
- }
- if (info.numFailed == 0) {
- return 0;
- }
- long when = info.restartTime();
- if (when <= now) {
- return 0;
- }
- return when - now;
- }
-
- /**
- * Returns whether there's a visible notification for this download
- */
- private boolean visibleNotification(int arrayPos) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- return info.hasCompletionNotification();
- }
-
- /**
- * Returns whether a file should be scanned
- */
- private boolean shouldScanFile(int arrayPos) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- return !info.mediaScanned
- && info.destination == Downloads.DESTINATION_EXTERNAL
- && Downloads.isStatusSuccess(info.status)
- && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(info.mimetype);
- }
-
- /**
- * Returns whether we have a live connection to the Media Scanner
- */
- private boolean mediaScannerConnected() {
- return mMediaScannerService != null;
- }
-
- /**
- * Attempts to scan the file if necessary.
- * Returns true if the file has been properly scanned.
- */
- private boolean scanFile(Cursor cursor, int arrayPos) {
- DownloadInfo info = (DownloadInfo) mDownloads.get(arrayPos);
- synchronized (this) {
- if (mMediaScannerService != null) {
- try {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "Scanning file " + info.filename);
- }
- mMediaScannerService.scanFile(info.filename, info.mimetype);
- if (cursor != null) {
- ContentValues values = new ContentValues();
- values.put(Constants.MEDIA_SCANNED, 1);
- getContentResolver().update(
- ContentUris.withAppendedId(Downloads.CONTENT_URI,
- cursor.getLong(cursor.getColumnIndexOrThrow(Downloads._ID))),
- values, null, null);
- }
- return true;
- } catch (RemoteException e) {
- if (Config.LOGD) {
- Log.d(Constants.TAG, "Failed to scan file " + info.filename);
- }
- }
- }
- }
- return false;
- }
-
-}