• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 
17 package com.android.providers.downloads;
18 
19 import android.app.DownloadManager;
20 import android.content.ActivityNotFoundException;
21 import android.content.BroadcastReceiver;
22 import android.content.ContentUris;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.database.Cursor;
27 import android.net.ConnectivityManager;
28 import android.net.NetworkInfo;
29 import android.net.Uri;
30 import android.provider.Downloads;
31 import android.util.Log;
32 
33 import com.google.common.annotations.VisibleForTesting;
34 
35 import java.io.File;
36 
37 /**
38  * Receives system broadcasts (boot, network connectivity)
39  */
40 public class DownloadReceiver extends BroadcastReceiver {
41     @VisibleForTesting
42     SystemFacade mSystemFacade = null;
43 
44     @Override
onReceive(Context context, Intent intent)45     public void onReceive(Context context, Intent intent) {
46         if (mSystemFacade == null) {
47             mSystemFacade = new RealSystemFacade(context);
48         }
49 
50         String action = intent.getAction();
51         if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
52             if (Constants.LOGVV) {
53                 Log.v(Constants.TAG, "Received broadcast intent for " +
54                         Intent.ACTION_BOOT_COMPLETED);
55             }
56             startService(context);
57         } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
58             if (Constants.LOGVV) {
59                 Log.v(Constants.TAG, "Received broadcast intent for " +
60                         Intent.ACTION_MEDIA_MOUNTED);
61             }
62             startService(context);
63         } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
64             final ConnectivityManager connManager = (ConnectivityManager) context
65                     .getSystemService(Context.CONNECTIVITY_SERVICE);
66             final NetworkInfo info = connManager.getActiveNetworkInfo();
67             if (info != null && info.isConnected()) {
68                 startService(context);
69             }
70         } else if (action.equals(Constants.ACTION_RETRY)) {
71             startService(context);
72         } else if (action.equals(Constants.ACTION_OPEN)
73                 || action.equals(Constants.ACTION_LIST)
74                 || action.equals(Constants.ACTION_HIDE)) {
75             handleNotificationBroadcast(context, intent);
76         }
77     }
78 
79     /**
80      * Handle any broadcast related to a system notification.
81      */
handleNotificationBroadcast(Context context, Intent intent)82     private void handleNotificationBroadcast(Context context, Intent intent) {
83         Uri uri = intent.getData();
84         String action = intent.getAction();
85         if (Constants.LOGVV) {
86             if (action.equals(Constants.ACTION_OPEN)) {
87                 Log.v(Constants.TAG, "Receiver open for " + uri);
88             } else if (action.equals(Constants.ACTION_LIST)) {
89                 Log.v(Constants.TAG, "Receiver list for " + uri);
90             } else { // ACTION_HIDE
91                 Log.v(Constants.TAG, "Receiver hide for " + uri);
92             }
93         }
94 
95         Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
96         if (cursor == null) {
97             return;
98         }
99         try {
100             if (!cursor.moveToFirst()) {
101                 return;
102             }
103 
104             if (action.equals(Constants.ACTION_OPEN)) {
105                 openDownload(context, cursor);
106                 hideNotification(context, uri, cursor);
107             } else if (action.equals(Constants.ACTION_LIST)) {
108                 sendNotificationClickedIntent(intent, cursor);
109             } else { // ACTION_HIDE
110                 hideNotification(context, uri, cursor);
111             }
112         } finally {
113             cursor.close();
114         }
115     }
116 
117     /**
118      * Hide a system notification for a download.
119      * @param uri URI to update the download
120      * @param cursor Cursor for reading the download's fields
121      */
hideNotification(Context context, Uri uri, Cursor cursor)122     private void hideNotification(Context context, Uri uri, Cursor cursor) {
123         mSystemFacade.cancelNotification(ContentUris.parseId(uri));
124 
125         int statusColumn = cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS);
126         int status = cursor.getInt(statusColumn);
127         int visibilityColumn =
128                 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_VISIBILITY);
129         int visibility = cursor.getInt(visibilityColumn);
130         if (Downloads.Impl.isStatusCompleted(status)
131                 && visibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
132             ContentValues values = new ContentValues();
133             values.put(Downloads.Impl.COLUMN_VISIBILITY,
134                     Downloads.Impl.VISIBILITY_VISIBLE);
135             context.getContentResolver().update(uri, values, null, null);
136         }
137     }
138 
139     /**
140      * Open the download that cursor is currently pointing to, since it's completed notification
141      * has been clicked.
142      */
openDownload(Context context, Cursor cursor)143     private void openDownload(Context context, Cursor cursor) {
144         String filename = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl._DATA));
145         String mimetype =
146             cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_MIME_TYPE));
147         Uri path = Uri.parse(filename);
148         // If there is no scheme, then it must be a file
149         if (path.getScheme() == null) {
150             path = Uri.fromFile(new File(filename));
151         }
152 
153         Intent activityIntent = new Intent(Intent.ACTION_VIEW);
154         mimetype = DownloadDrmHelper.getOriginalMimeType(context, filename, mimetype);
155         activityIntent.setDataAndType(path, mimetype);
156         activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
157         try {
158             context.startActivity(activityIntent);
159         } catch (ActivityNotFoundException ex) {
160             Log.d(Constants.TAG, "no activity for " + mimetype, ex);
161         }
162     }
163 
164     /**
165      * Notify the owner of a running download that its notification was clicked.
166      * @param intent the broadcast intent sent by the notification manager
167      * @param cursor Cursor for reading the download's fields
168      */
sendNotificationClickedIntent(Intent intent, Cursor cursor)169     private void sendNotificationClickedIntent(Intent intent, Cursor cursor) {
170         String pckg = cursor.getString(
171                 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE));
172         if (pckg == null) {
173             return;
174         }
175 
176         String clazz = cursor.getString(
177                 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_NOTIFICATION_CLASS));
178         boolean isPublicApi =
179                 cursor.getInt(cursor.getColumnIndex(Downloads.Impl.COLUMN_IS_PUBLIC_API)) != 0;
180 
181         Intent appIntent = null;
182         if (isPublicApi) {
183             appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
184             appIntent.setPackage(pckg);
185             // send id of the items clicked on.
186             if (intent.getBooleanExtra("multiple", false)) {
187                 // broadcast received saying click occurred on a notification with multiple titles.
188                 // don't include any ids at all - let the caller query all downloads belonging to it
189                 // TODO modify the broadcast to include ids of those multiple notifications.
190             } else {
191                 appIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
192                         new long[] {
193                                 cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.Impl._ID))});
194             }
195         } else { // legacy behavior
196             if (clazz == null) {
197                 return;
198             }
199             appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
200             appIntent.setClassName(pckg, clazz);
201             if (intent.getBooleanExtra("multiple", true)) {
202                 appIntent.setData(Downloads.Impl.CONTENT_URI);
203             } else {
204                 long downloadId = cursor.getLong(cursor.getColumnIndexOrThrow(Downloads.Impl._ID));
205                 appIntent.setData(
206                         ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, downloadId));
207             }
208         }
209 
210         mSystemFacade.sendBroadcast(appIntent);
211     }
212 
startService(Context context)213     private void startService(Context context) {
214         context.startService(new Intent(context, DownloadService.class));
215     }
216 }
217