am cc167f03: (-s ours) am bb611587: (-s ours) Import translations. DO NOT MERGE
[android/platform/packages/providers/DownloadProvider.git] / src / com / android / providers / downloads / DownloadReceiver.java
index 03a3718..28e2a67 100644 (file)
 
 package com.android.providers.downloads;
 
-import android.app.NotificationManager;
-import android.content.ActivityNotFoundException;
+import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
+import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION;
+import static com.android.providers.downloads.Constants.TAG;
+
+import android.app.DownloadManager;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 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.database.Cursor;
-import android.provider.Downloads;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
-import android.util.Config;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.Downloads;
+import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
+import android.widget.Toast;
 
-import java.io.File;
-import java.util.List;
+import com.google.common.annotations.VisibleForTesting;
 
 /**
  * Receives system broadcasts (boot, network connectivity)
  */
 public class DownloadReceiver extends BroadcastReceiver {
+    private static Handler sAsyncHandler;
 
-    public void onReceive(Context context, Intent intent) {
-        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
-            if (Constants.LOGVV) {
-                Log.v(Constants.TAG, "Receiver onBoot");
-            }
-            context.startService(new Intent(context, DownloadService.class));
-        } else if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-            if (Constants.LOGVV) {
-                Log.v(Constants.TAG, "Receiver onConnectivity");
-            }
-            NetworkInfo info = (NetworkInfo)
-                    intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+    static {
+        final HandlerThread thread = new HandlerThread("DownloadReceiver");
+        thread.start();
+        sAsyncHandler = new Handler(thread.getLooper());
+    }
+
+    @VisibleForTesting
+    SystemFacade mSystemFacade = null;
+
+    @Override
+    public void onReceive(final Context context, final Intent intent) {
+        if (mSystemFacade == null) {
+            mSystemFacade = new RealSystemFacade(context);
+        }
+
+        final String action = intent.getAction();
+        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+            startService(context);
+
+        } else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
+            startService(context);
+
+        } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
+            final ConnectivityManager connManager = (ConnectivityManager) context
+                    .getSystemService(Context.CONNECTIVITY_SERVICE);
+            final NetworkInfo info = connManager.getActiveNetworkInfo();
             if (info != null && info.isConnected()) {
-                context.startService(new Intent(context, DownloadService.class));
+                startService(context);
             }
-        } else if (intent.getAction().equals(Constants.ACTION_RETRY)) {
-            if (Constants.LOGVV) {
-                Log.v(Constants.TAG, "Receiver retry");
-            }
-            context.startService(new Intent(context, DownloadService.class));
-        } else if (intent.getAction().equals(Constants.ACTION_OPEN)
-                || intent.getAction().equals(Constants.ACTION_LIST)) {
-            if (Constants.LOGVV) {
-                if (intent.getAction().equals(Constants.ACTION_OPEN)) {
-                    Log.v(Constants.TAG, "Receiver open for " + intent.getData());
-                } else {
-                    Log.v(Constants.TAG, "Receiver list for " + intent.getData());
+
+        } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
+            final PendingResult result = goAsync();
+            sAsyncHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleUidRemoved(context, intent);
+                    result.finish();
                 }
-            }
-            Cursor cursor = context.getContentResolver().query(
-                    intent.getData(), null, null, null, null);
-            if (cursor != null) {
-                if (cursor.moveToFirst()) {
-                    int statusColumn = cursor.getColumnIndexOrThrow(Downloads.STATUS);
-                    int status = cursor.getInt(statusColumn);
-                    int visibilityColumn = cursor.getColumnIndexOrThrow(Downloads.VISIBILITY);
-                    int visibility = cursor.getInt(visibilityColumn);
-                    if (Downloads.isStatusCompleted(status)
-                            && visibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
-                        ContentValues values = new ContentValues();
-                        values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE);
-                        context.getContentResolver().update(intent.getData(), values, null, null);
-                    }
+            });
+
+        } else if (Constants.ACTION_RETRY.equals(action)) {
+            startService(context);
 
-                    if (intent.getAction().equals(Constants.ACTION_OPEN)) {
-                        int filenameColumn = cursor.getColumnIndexOrThrow(Downloads._DATA);
-                        int mimetypeColumn = cursor.getColumnIndexOrThrow(Downloads.MIMETYPE);
-                        String filename = cursor.getString(filenameColumn);
-                        String mimetype = cursor.getString(mimetypeColumn);
-                        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);
-                        activityIntent.setDataAndType(path, mimetype);
-                        activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                        try {
-                            context.startActivity(activityIntent);
-                        } catch (ActivityNotFoundException ex) {
-                            if (Config.LOGD) {
-                                Log.d(Constants.TAG, "no activity for " + mimetype, ex);
-                            }
-                            // nothing anyone can do about this, but we're in a clean state,
-                            //     swallow the exception entirely
-                        }
-                    } else {
-                        int packageColumn =
-                                cursor.getColumnIndexOrThrow(Downloads.NOTIFICATION_PACKAGE);
-                        int classColumn =
-                                cursor.getColumnIndexOrThrow(Downloads.NOTIFICATION_CLASS);
-                        String pckg = cursor.getString(packageColumn);
-                        String clazz = cursor.getString(classColumn);
-                        if (pckg != null && clazz != null) {
-                            Intent appIntent = new Intent(Downloads.NOTIFICATION_CLICKED_ACTION);
-                            appIntent.setClassName(pckg, clazz);
-                            if (intent.getBooleanExtra("multiple", true)) {
-                                appIntent.setData(Downloads.CONTENT_URI);
-                            } else {
-                                appIntent.setData(intent.getData());
-                            }
-                            context.sendBroadcast(appIntent);
-                        }
+        } else if (Constants.ACTION_OPEN.equals(action)
+                || Constants.ACTION_LIST.equals(action)
+                || Constants.ACTION_HIDE.equals(action)) {
+
+            final PendingResult result = goAsync();
+            if (result == null) {
+                // TODO: remove this once test is refactored
+                handleNotificationBroadcast(context, intent);
+            } else {
+                sAsyncHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        handleNotificationBroadcast(context, intent);
+                        result.finish();
                     }
-                }
-                cursor.close();
+                });
             }
-            NotificationManager notMgr = (NotificationManager) context
-                    .getSystemService(Context.NOTIFICATION_SERVICE);
-            if (notMgr != null) {
-                notMgr.cancel((int) ContentUris.parseId(intent.getData()));
+        }
+    }
+
+    private void handleUidRemoved(Context context, Intent intent) {
+        final ContentResolver resolver = context.getContentResolver();
+
+        final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+        final int count = resolver.delete(
+                Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, Constants.UID + "=" + uid, null);
+
+        if (count > 0) {
+            Slog.d(TAG, "Deleted " + count + " downloads owned by UID " + uid);
+        }
+    }
+
+    /**
+     * Handle any broadcast related to a system notification.
+     */
+    private void handleNotificationBroadcast(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (Constants.ACTION_LIST.equals(action)) {
+            final long[] ids = intent.getLongArrayExtra(
+                    DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
+            sendNotificationClickedIntent(context, ids);
+
+        } else if (Constants.ACTION_OPEN.equals(action)) {
+            final long id = ContentUris.parseId(intent.getData());
+            openDownload(context, id);
+            hideNotification(context, id);
+
+        } else if (Constants.ACTION_HIDE.equals(action)) {
+            final long id = ContentUris.parseId(intent.getData());
+            hideNotification(context, id);
+        }
+    }
+
+    /**
+     * Mark the given {@link DownloadManager#COLUMN_ID} as being acknowledged by
+     * user so it's not renewed later.
+     */
+    private void hideNotification(Context context, long id) {
+        final int status;
+        final int visibility;
+
+        final Uri uri = ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
+        final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
+        try {
+            if (cursor.moveToFirst()) {
+                status = getInt(cursor, Downloads.Impl.COLUMN_STATUS);
+                visibility = getInt(cursor, Downloads.Impl.COLUMN_VISIBILITY);
+            } else {
+                Log.w(TAG, "Missing details for download " + id);
+                return;
             }
-        } else if (intent.getAction().equals(Constants.ACTION_HIDE)) {
-            if (Constants.LOGVV) {
-                Log.v(Constants.TAG, "Receiver hide for " + intent.getData());
+        } finally {
+            cursor.close();
+        }
+
+        if (Downloads.Impl.isStatusCompleted(status) &&
+                (visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED
+                || visibility == VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION)) {
+            final ContentValues values = new ContentValues();
+            values.put(Downloads.Impl.COLUMN_VISIBILITY,
+                    Downloads.Impl.VISIBILITY_VISIBLE);
+            context.getContentResolver().update(uri, values, null, null);
+        }
+    }
+
+    /**
+     * Start activity to display the file represented by the given
+     * {@link DownloadManager#COLUMN_ID}.
+     */
+    private void openDownload(Context context, long id) {
+        if (!OpenHelper.startViewIntent(context, id, Intent.FLAG_ACTIVITY_NEW_TASK)) {
+            Toast.makeText(context, R.string.download_no_application_title, Toast.LENGTH_SHORT)
+                    .show();
+        }
+    }
+
+    /**
+     * Notify the owner of a running download that its notification was clicked.
+     */
+    private void sendNotificationClickedIntent(Context context, long[] ids) {
+        final String packageName;
+        final String clazz;
+        final boolean isPublicApi;
+
+        final Uri uri = ContentUris.withAppendedId(
+                Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, ids[0]);
+        final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
+        try {
+            if (cursor.moveToFirst()) {
+                packageName = getString(cursor, Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
+                clazz = getString(cursor, Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
+                isPublicApi = getInt(cursor, Downloads.Impl.COLUMN_IS_PUBLIC_API) != 0;
+            } else {
+                Log.w(TAG, "Missing details for download " + ids[0]);
+                return;
             }
-            Cursor cursor = context.getContentResolver().query(
-                    intent.getData(), null, null, null, null);
-            if (cursor != null) {
-                if (cursor.moveToFirst()) {
-                    int statusColumn = cursor.getColumnIndexOrThrow(Downloads.STATUS);
-                    int status = cursor.getInt(statusColumn);
-                    int visibilityColumn = cursor.getColumnIndexOrThrow(Downloads.VISIBILITY);
-                    int visibility = cursor.getInt(visibilityColumn);
-                    if (Downloads.isStatusCompleted(status)
-                            && visibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
-                        ContentValues values = new ContentValues();
-                        values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE);
-                        context.getContentResolver().update(intent.getData(), values, null, null);
-                    }
-                }
-                cursor.close();
+        } finally {
+            cursor.close();
+        }
+
+        if (TextUtils.isEmpty(packageName)) {
+            Log.w(TAG, "Missing package; skipping broadcast");
+            return;
+        }
+
+        Intent appIntent = null;
+        if (isPublicApi) {
+            appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
+            appIntent.setPackage(packageName);
+            appIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, ids);
+
+        } else { // legacy behavior
+            if (TextUtils.isEmpty(clazz)) {
+                Log.w(TAG, "Missing class; skipping broadcast");
+                return;
+            }
+
+            appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
+            appIntent.setClassName(packageName, clazz);
+            appIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, ids);
+
+            if (ids.length == 1) {
+                appIntent.setData(uri);
+            } else {
+                appIntent.setData(Downloads.Impl.CONTENT_URI);
             }
         }
+
+        mSystemFacade.sendBroadcast(appIntent);
+    }
+
+    private static String getString(Cursor cursor, String col) {
+        return cursor.getString(cursor.getColumnIndexOrThrow(col));
+    }
+
+    private static int getInt(Cursor cursor, String col) {
+        return cursor.getInt(cursor.getColumnIndexOrThrow(col));
+    }
+
+    private void startService(Context context) {
+        context.startService(new Intent(context, DownloadService.class));
     }
 }