diff options
author | Steve Kondik <shade@chemlab.org> | 2012-11-18 22:33:11 -0800 |
---|---|---|
committer | Steve Kondik <shade@chemlab.org> | 2012-11-18 22:33:11 -0800 |
commit | 516f77f561e1388ae94fe1a26d2a31960074158a (patch) | |
tree | a01a9e9deb200f593008b0763f475471502b6728 /src/com/android/providers/downloads | |
parent | 59a409bbc018124a275fbedc9e7d0166e0e18cca (diff) | |
parent | 2fa007ef678b2283d47d007aa3dc91af683cc52c (diff) | |
download | android_packages_providers_DownloadProvider-516f77f561e1388ae94fe1a26d2a31960074158a.tar.gz android_packages_providers_DownloadProvider-516f77f561e1388ae94fe1a26d2a31960074158a.tar.bz2 android_packages_providers_DownloadProvider-516f77f561e1388ae94fe1a26d2a31960074158a.zip |
Merge branch 'jb-mr1-release' of https://android.googlesource.com/platform/packages/providers/DownloadProvider into HEADcm-10.1-M1
Diffstat (limited to 'src/com/android/providers/downloads')
9 files changed, 245 insertions, 97 deletions
diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java index 28adfbdf..5172b696 100644 --- a/src/com/android/providers/downloads/DownloadInfo.java +++ b/src/com/android/providers/downloads/DownloadInfo.java @@ -36,7 +36,6 @@ import android.util.Pair; import com.android.internal.util.IndentingPrintWriter; -import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -316,11 +315,8 @@ public class DownloadInfo { // is the media mounted? return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); case Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR: - // should check space to make sure it is worth retrying the download. - // but thats the first thing done by the thread when it retries to download - // it will fail pretty quickly if there is no space. - // so, it is not that bad to skip checking space availability here. - return true; + // avoids repetition of retrying download + return false; } return false; } @@ -345,7 +341,7 @@ public class DownloadInfo { */ public int checkCanUseNetwork() { final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mUid); - if (info == null) { + if (info == null || !info.isConnected()) { return NETWORK_NO_CONNECTION; } if (DetailedState.BLOCKED.equals(info.getDetailedState())) { @@ -425,6 +421,9 @@ public class DownloadInfo { case ConnectivityManager.TYPE_WIFI: return DownloadManager.Request.NETWORK_WIFI; + case ConnectivityManager.TYPE_BLUETOOTH: + return DownloadManager.Request.NETWORK_BLUETOOTH; + default: return 0; } @@ -576,4 +575,24 @@ public class DownloadInfo { StorageManager.getInstance(mContext)); mSystemFacade.startThread(downloader); } + + /** + * Query and return status of requested download. + */ + public static int queryDownloadStatus(ContentResolver resolver, long id) { + final Cursor cursor = resolver.query( + ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id), + new String[] { Downloads.Impl.COLUMN_STATUS }, null, null, null); + try { + if (cursor.moveToFirst()) { + return cursor.getInt(0); + } else { + // TODO: increase strictness of value returned for unknown + // downloads; this is safe default for now. + return Downloads.Impl.STATUS_PENDING; + } + } finally { + cursor.close(); + } + } } diff --git a/src/com/android/providers/downloads/DownloadNotification.java b/src/com/android/providers/downloads/DownloadNotification.java index bbd39f60..f5778e79 100644 --- a/src/com/android/providers/downloads/DownloadNotification.java +++ b/src/com/android/providers/downloads/DownloadNotification.java @@ -17,6 +17,7 @@ package com.android.providers.downloads; import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ContentUris; import android.content.Context; @@ -38,9 +39,9 @@ import java.util.HashMap; */ class DownloadNotification { - Context mContext; - HashMap <String, NotificationItem> mNotifications; - private SystemFacade mSystemFacade; + private Context mContext; + private NotificationManager mNotifManager; + private HashMap<String, NotificationItem> mNotifications; /** Time when each {@link DownloadInfo#mId} was first shown. */ private SparseLongArray mFirstShown = new SparseLongArray(); @@ -102,7 +103,8 @@ class DownloadNotification { */ DownloadNotification(Context ctx, SystemFacade systemFacade) { mContext = ctx; - mSystemFacade = systemFacade; + mNotifManager = (NotificationManager) mContext.getSystemService( + Context.NOTIFICATION_SERVICE); mNotifications = new HashMap<String, NotificationItem>(); } @@ -207,8 +209,7 @@ class DownloadNotification { builder.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0)); - mSystemFacade.postNotification(item.mId, builder.getNotification()); - + mNotifManager.notify(item.mId, builder.build()); } } @@ -262,7 +263,7 @@ class DownloadNotification { intent.setData(contentUri); builder.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0)); - mSystemFacade.postNotification(id, builder.getNotification()); + mNotifManager.notify((int) id, builder.build()); } private boolean isActiveAndVisible(DownloadInfo download) { diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java index 26ad992e..7469508d 100644 --- a/src/com/android/providers/downloads/DownloadReceiver.java +++ b/src/com/android/providers/downloads/DownloadReceiver.java @@ -17,6 +17,7 @@ package com.android.providers.downloads; import android.app.DownloadManager; +import android.app.NotificationManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ContentUris; @@ -27,13 +28,12 @@ import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; +import android.provider.BaseColumns; import android.provider.Downloads; import android.util.Log; import com.google.common.annotations.VisibleForTesting; -import java.io.File; - /** * Receives system broadcasts (boot, network connectivity) */ @@ -120,7 +120,9 @@ public class DownloadReceiver extends BroadcastReceiver { * @param cursor Cursor for reading the download's fields */ private void hideNotification(Context context, Uri uri, Cursor cursor) { - mSystemFacade.cancelNotification(ContentUris.parseId(uri)); + final NotificationManager notifManager = (NotificationManager) context.getSystemService( + Context.NOTIFICATION_SERVICE); + notifManager.cancel((int) ContentUris.parseId(uri)); int statusColumn = cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS); int status = cursor.getInt(statusColumn); @@ -141,23 +143,13 @@ public class DownloadReceiver extends BroadcastReceiver { * has been clicked. */ private void openDownload(Context context, Cursor cursor) { - String filename = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl._DATA)); - String mimetype = - cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_MIME_TYPE)); - Uri path = Uri.parse(filename); - // If there is no scheme, then it must be a file - if (path.getScheme() == null) { - path = Uri.fromFile(new File(filename)); - } - - Intent activityIntent = new Intent(Intent.ACTION_VIEW); - mimetype = DownloadDrmHelper.getOriginalMimeType(context, filename, mimetype); - activityIntent.setDataAndType(path, mimetype); - activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final long id = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID)); + final Intent intent = OpenHelper.buildViewIntent(context, id); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { - context.startActivity(activityIntent); + context.startActivity(intent); } catch (ActivityNotFoundException ex) { - Log.d(Constants.TAG, "no activity for " + mimetype, ex); + Log.d(Constants.TAG, "no activity for " + intent, ex); } } diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java index 3b566f8e..8380830a 100644 --- a/src/com/android/providers/downloads/DownloadService.java +++ b/src/com/android/providers/downloads/DownloadService.java @@ -19,6 +19,7 @@ package com.android.providers.downloads; import static com.android.providers.downloads.Constants.TAG; import android.app.AlarmManager; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.ComponentName; @@ -38,7 +39,6 @@ import android.os.RemoteException; import android.provider.Downloads; import android.text.TextUtils; import android.util.Log; -import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import com.google.android.collect.Maps; @@ -66,6 +66,7 @@ public class DownloadService extends Service { /** Class to handle Notification Manager updates */ private DownloadNotification mNotifier; + private NotificationManager mNotifManager; /** * The Service's view of the list of downloads, mapping download IDs to the corresponding info @@ -222,7 +223,9 @@ public class DownloadService extends Service { mMediaScannerConnection = new MediaScannerConnection(); mNotifier = new DownloadNotification(this, mSystemFacade); - mSystemFacade.cancelAllNotifications(); + mNotifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mNotifManager.cancelAll(); + mStorageManager = StorageManager.getInstance(getApplicationContext()); updateFromProvider(); } @@ -465,7 +468,7 @@ public class DownloadService extends Service { !Downloads.Impl.isStatusCompleted(oldStatus) && Downloads.Impl.isStatusCompleted(info.mStatus); if (lostVisibility || justCompleted) { - mSystemFacade.cancelNotification(info.mId); + mNotifManager.cancel((int) info.mId); } info.startIfReady(now, mStorageManager); @@ -476,17 +479,16 @@ public class DownloadService extends Service { */ private void deleteDownloadLocked(long id) { DownloadInfo info = mDownloads.get(id); - if (info.shouldScanFile()) { - scanFile(info, false, false); - } if (info.mStatus == Downloads.Impl.STATUS_RUNNING) { info.mStatus = Downloads.Impl.STATUS_CANCELED; } if (info.mDestination != Downloads.Impl.DESTINATION_EXTERNAL && info.mFileName != null) { - Slog.d(TAG, "deleteDownloadLocked() deleting " + info.mFileName); + if (Constants.LOGVV) { + Log.d(TAG, "deleteDownloadLocked() deleting " + info.mFileName); + } new File(info.mFileName).delete(); } - mSystemFacade.cancelNotification(info.mId); + mNotifManager.cancel((int) info.mId); mDownloads.remove(info.mId); } @@ -559,7 +561,9 @@ public class DownloadService extends Service { private void deleteFileIfExists(String path) { try { if (!TextUtils.isEmpty(path)) { - Slog.d(TAG, "deleteFileIfExists() deleting " + path); + if (Constants.LOGVV) { + Log.d(TAG, "deleteFileIfExists() deleting " + path); + } File file = new File(path); file.delete(); } diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index bd91eaa1..e74d5c72 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -33,7 +33,6 @@ import android.provider.Downloads; import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import android.util.Slog; import org.apache.http.Header; import org.apache.http.HttpResponse; @@ -131,6 +130,21 @@ public class DownloadThread extends Thread { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + try { + runInternal(); + } finally { + DownloadHandler.getInstance().dequeueDownload(mInfo.mId); + } + } + + private void runInternal() { + // Skip when download already marked as finished; this download was + // probably started again while racing with UpdateThread. + if (DownloadInfo.queryDownloadStatus(mContext.getContentResolver(), mInfo.mId) + == Downloads.Impl.STATUS_SUCCESS) { + Log.d(TAG, "Download " + mInfo.mId + " already finished; skipping"); + return; + } State state = new State(mInfo); AndroidHttpClient client = null; @@ -211,7 +225,6 @@ public class DownloadThread extends Thread { notifyDownloadCompleted(finalStatus, state.mCountRetry, state.mRetryAfter, state.mGotData, state.mFilename, state.mNewUri, state.mMimeType, errorMsg); - DownloadHandler.getInstance().dequeueDownload(mInfo.mId); netPolicy.unregisterListener(mPolicyListener); @@ -330,7 +343,9 @@ public class DownloadThread extends Thread { closeDestination(state); if (state.mFilename != null && Downloads.Impl.isStatusError(finalStatus)) { - Slog.d(TAG, "cleanupDestination() deleting " + state.mFilename); + if (Constants.LOGVV) { + Log.d(TAG, "cleanupDestination() deleting " + state.mFilename); + } new File(state.mFilename).delete(); state.mFilename = null; } @@ -847,8 +862,10 @@ public class DownloadThread extends Thread { long fileLength = f.length(); if (fileLength == 0) { // The download hadn't actually started, we can restart from scratch - Slog.d(TAG, "setupDestinationFile() found fileLength=0, deleting " - + state.mFilename); + if (Constants.LOGVV) { + Log.d(TAG, "setupDestinationFile() found fileLength=0, deleting " + + state.mFilename); + } f.delete(); state.mFilename = null; if (Constants.LOGV) { @@ -857,8 +874,10 @@ public class DownloadThread extends Thread { } } else if (mInfo.mETag == null && !mInfo.mNoIntegrity) { // This should've been caught upon failure - Slog.d(TAG, "setupDestinationFile() unable to resume download, deleting " - + state.mFilename); + if (Constants.LOGVV) { + Log.d(TAG, "setupDestinationFile() unable to resume download, deleting " + + state.mFilename); + } f.delete(); throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME, "Trying to resume a download that can't be resumed"); diff --git a/src/com/android/providers/downloads/OpenHelper.java b/src/com/android/providers/downloads/OpenHelper.java new file mode 100644 index 00000000..7eca95c9 --- /dev/null +++ b/src/com/android/providers/downloads/OpenHelper.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2012 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 static android.app.DownloadManager.COLUMN_LOCAL_FILENAME; +import static android.app.DownloadManager.COLUMN_LOCAL_URI; +import static android.app.DownloadManager.COLUMN_MEDIA_TYPE; +import static android.app.DownloadManager.COLUMN_URI; +import static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI; + +import android.app.DownloadManager; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.Downloads.Impl.RequestHeaders; + +public class OpenHelper { + /** + * Build an {@link Intent} to view the download at current {@link Cursor} + * position, handling subtleties around installing packages. + */ + public static Intent buildViewIntent(Context context, long id) { + final DownloadManager downManager = (DownloadManager) context.getSystemService( + Context.DOWNLOAD_SERVICE); + downManager.setAccessAllDownloads(true); + + final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id)); + try { + if (!cursor.moveToFirst()) { + throw new IllegalArgumentException("Missing download " + id); + } + + final Uri localUri = getCursorUri(cursor, COLUMN_LOCAL_URI); + final String filename = getCursorString(cursor, COLUMN_LOCAL_FILENAME); + String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE); + mimeType = DownloadDrmHelper.getOriginalMimeType(context, filename, mimeType); + + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + if ("application/vnd.android.package-archive".equals(mimeType)) { + // PackageInstaller doesn't like content URIs, so open file + intent.setDataAndType(localUri, mimeType); + + // Also splice in details about where it came from + final Uri remoteUri = getCursorUri(cursor, COLUMN_URI); + intent.putExtra(Intent.EXTRA_ORIGINATING_URI, remoteUri); + intent.putExtra(Intent.EXTRA_REFERRER, getRefererUri(context, id)); + intent.putExtra(Intent.EXTRA_ORIGINATING_UID, getOriginatingUid(context, id)); + } else if ("file".equals(localUri.getScheme())) { + intent.setDataAndType( + ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id), mimeType); + } else { + intent.setDataAndType(localUri, mimeType); + } + + return intent; + } finally { + cursor.close(); + } + } + + private static Uri getRefererUri(Context context, long id) { + final Uri headersUri = Uri.withAppendedPath( + ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id), + RequestHeaders.URI_SEGMENT); + final Cursor headers = context.getContentResolver() + .query(headersUri, null, null, null, null); + try { + while (headers.moveToNext()) { + final String header = getCursorString(headers, RequestHeaders.COLUMN_HEADER); + if ("Referer".equalsIgnoreCase(header)) { + return getCursorUri(headers, RequestHeaders.COLUMN_VALUE); + } + } + } finally { + headers.close(); + } + return null; + } + + private static int getOriginatingUid(Context context, long id) { + final Uri uri = ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id); + final Cursor cursor = context.getContentResolver().query(uri, new String[]{Constants.UID}, + null, null, null); + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + return cursor.getInt(cursor.getColumnIndexOrThrow(Constants.UID)); + } + } finally { + cursor.close(); + } + } + return -1; + } + + private static String getCursorString(Cursor cursor, String column) { + return cursor.getString(cursor.getColumnIndexOrThrow(column)); + } + + private static Uri getCursorUri(Cursor cursor, String column) { + return Uri.parse(getCursorString(cursor, column)); + } + + private static long getCursorLong(Cursor cursor, String column) { + return cursor.getLong(cursor.getColumnIndexOrThrow(column)); + } +} diff --git a/src/com/android/providers/downloads/RealSystemFacade.java b/src/com/android/providers/downloads/RealSystemFacade.java index 6580f909..228c7165 100644 --- a/src/com/android/providers/downloads/RealSystemFacade.java +++ b/src/com/android/providers/downloads/RealSystemFacade.java @@ -1,26 +1,35 @@ +/* + * 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 android.app.DownloadManager; -import android.app.Notification; -import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; import android.telephony.TelephonyManager; import android.util.Log; class RealSystemFacade implements SystemFacade { private Context mContext; - private NotificationManager mNotificationManager; public RealSystemFacade(Context context) { mContext = context; - mNotificationManager = (NotificationManager) - mContext.getSystemService(Context.NOTIFICATION_SERVICE); } public long currentTimeMillis() { @@ -85,26 +94,6 @@ class RealSystemFacade implements SystemFacade { } @Override - public void postNotification(long id, Notification notification) { - /** - * TODO: The system notification manager takes ints, not longs, as IDs, but the download - * manager uses IDs take straight from the database, which are longs. This will have to be - * dealt with at some point. - */ - mNotificationManager.notify((int) id, notification); - } - - @Override - public void cancelNotification(long id) { - mNotificationManager.cancel((int) id); - } - - @Override - public void cancelAllNotifications() { - mNotificationManager.cancelAll(); - } - - @Override public void startThread(Thread thread) { thread.start(); } diff --git a/src/com/android/providers/downloads/StorageManager.java b/src/com/android/providers/downloads/StorageManager.java index 4b51921f..915d141b 100644 --- a/src/com/android/providers/downloads/StorageManager.java +++ b/src/com/android/providers/downloads/StorageManager.java @@ -30,7 +30,6 @@ import android.os.StatFs; import android.provider.Downloads; import android.text.TextUtils; import android.util.Log; -import android.util.Slog; import com.android.internal.R; @@ -342,9 +341,9 @@ class StorageManager { if (TextUtils.isEmpty(data)) continue; File file = new File(data); - if (true || Constants.LOGV) { - Slog.d(Constants.TAG, "purging " + file.getAbsolutePath() + " for " + - file.length() + " bytes"); + if (Constants.LOGV) { + Log.d(Constants.TAG, "purging " + file.getAbsolutePath() + " for " + + file.length() + " bytes"); } totalFreed += file.length(); file.delete(); @@ -416,7 +415,9 @@ class StorageManager { try { final StructStat stat = Libcore.os.stat(path); if (stat.st_uid == myUid) { - Slog.d(TAG, "deleting spurious file " + path); + if (Constants.LOGVV) { + Log.d(TAG, "deleting spurious file " + path); + } file.delete(); } } catch (ErrnoException e) { diff --git a/src/com/android/providers/downloads/SystemFacade.java b/src/com/android/providers/downloads/SystemFacade.java index d1439354..fda97e08 100644 --- a/src/com/android/providers/downloads/SystemFacade.java +++ b/src/com/android/providers/downloads/SystemFacade.java @@ -1,12 +1,25 @@ +/* + * 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 android.app.Notification; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.net.NetworkInfo; - interface SystemFacade { /** * @see System#currentTimeMillis() @@ -50,21 +63,6 @@ interface SystemFacade { public boolean userOwnsPackage(int uid, String pckg) throws NameNotFoundException; /** - * Post a system notification to the NotificationManager. - */ - public void postNotification(long id, Notification notification); - - /** - * Cancel a system notification. - */ - public void cancelNotification(long id); - - /** - * Cancel all system notifications. - */ - public void cancelAllNotifications(); - - /** * Start a thread. */ public void startThread(Thread thread); |