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
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 static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
20 import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION;
21 import static com.android.providers.downloads.Constants.TAG;
22
23 import android.app.DownloadManager;
24 import android.content.BroadcastReceiver;
25 import android.content.ContentResolver;
26 import android.content.ContentUris;
27 import android.content.ContentValues;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.database.Cursor;
31 import android.net.ConnectivityManager;
32 import android.net.NetworkInfo;
33 import android.net.Uri;
34 import android.os.Handler;
35 import android.os.HandlerThread;
36 import android.provider.Downloads;
37 import android.text.TextUtils;
38 import android.util.Log;
39 import android.util.Slog;
40 import android.widget.Toast;
41
42 import com.google.common.annotations.VisibleForTesting;
43
44 /**
45  * Receives system broadcasts (boot, network connectivity)
46  */
47 public class DownloadReceiver extends BroadcastReceiver {
48     private static Handler sAsyncHandler;
49
50     static {
51         final HandlerThread thread = new HandlerThread("DownloadReceiver");
52         thread.start();
53         sAsyncHandler = new Handler(thread.getLooper());
54     }
55
56     @VisibleForTesting
57     SystemFacade mSystemFacade = null;
58
59     @Override
60     public void onReceive(final Context context, final Intent intent) {
61         if (mSystemFacade == null) {
62             mSystemFacade = new RealSystemFacade(context);
63         }
64
65         final String action = intent.getAction();
66         if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
67             startService(context);
68
69         } else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
70             startService(context);
71
72         } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
73             final ConnectivityManager connManager = (ConnectivityManager) context
74                     .getSystemService(Context.CONNECTIVITY_SERVICE);
75             final NetworkInfo info = connManager.getActiveNetworkInfo();
76             if (info != null && info.isConnected()) {
77                 startService(context);
78             }
79
80         } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
81             final PendingResult result = goAsync();
82             sAsyncHandler.post(new Runnable() {
83                 @Override
84                 public void run() {
85                     handleUidRemoved(context, intent);
86                     result.finish();
87                 }
88             });
89
90         } else if (Constants.ACTION_RETRY.equals(action)) {
91             startService(context);
92
93         } else if (Constants.ACTION_OPEN.equals(action)
94                 || Constants.ACTION_LIST.equals(action)
95                 || Constants.ACTION_HIDE.equals(action)) {
96
97             final PendingResult result = goAsync();
98             if (result == null) {
99                 // TODO: remove this once test is refactored
100                 handleNotificationBroadcast(context, intent);
101             } else {
102                 sAsyncHandler.post(new Runnable() {
103                     @Override
104                     public void run() {
105                         handleNotificationBroadcast(context, intent);
106                         result.finish();
107                     }
108                 });
109             }
110         }
111     }
112
113     private void handleUidRemoved(Context context, Intent intent) {
114         final ContentResolver resolver = context.getContentResolver();
115
116         final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
117         final int count = resolver.delete(
118                 Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, Constants.UID + "=" + uid, null);
119
120         if (count > 0) {
121             Slog.d(TAG, "Deleted " + count + " downloads owned by UID " + uid);
122         }
123     }
124
125     /**
126      * Handle any broadcast related to a system notification.
127      */
128     private void handleNotificationBroadcast(Context context, Intent intent) {
129         final String action = intent.getAction();
130         if (Constants.ACTION_LIST.equals(action)) {
131             final long[] ids = intent.getLongArrayExtra(
132                     DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
133             sendNotificationClickedIntent(context, ids);
134
135         } else if (Constants.ACTION_OPEN.equals(action)) {
136             final long id = ContentUris.parseId(intent.getData());
137             openDownload(context, id);
138             hideNotification(context, id);
139
140         } else if (Constants.ACTION_HIDE.equals(action)) {
141             final long id = ContentUris.parseId(intent.getData());
142             hideNotification(context, id);
143         }
144     }
145
146     /**
147      * Mark the given {@link DownloadManager#COLUMN_ID} as being acknowledged by
148      * user so it's not renewed later.
149      */
150     private void hideNotification(Context context, long id) {
151         final int status;
152         final int visibility;
153
154         final Uri uri = ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
155         final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
156         try {
157             if (cursor.moveToFirst()) {
158                 status = getInt(cursor, Downloads.Impl.COLUMN_STATUS);
159                 visibility = getInt(cursor, Downloads.Impl.COLUMN_VISIBILITY);
160             } else {
161                 Log.w(TAG, "Missing details for download " + id);
162                 return;
163             }
164         } finally {
165             cursor.close();
166         }
167
168         if (Downloads.Impl.isStatusCompleted(status) &&
169                 (visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED
170                 || visibility == VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION)) {
171             final ContentValues values = new ContentValues();
172             values.put(Downloads.Impl.COLUMN_VISIBILITY,
173                     Downloads.Impl.VISIBILITY_VISIBLE);
174             context.getContentResolver().update(uri, values, null, null);
175         }
176     }
177
178     /**
179      * Start activity to display the file represented by the given
180      * {@link DownloadManager#COLUMN_ID}.
181      */
182     private void openDownload(Context context, long id) {
183         if (!OpenHelper.startViewIntent(context, id, Intent.FLAG_ACTIVITY_NEW_TASK)) {
184             Toast.makeText(context, R.string.download_no_application_title, Toast.LENGTH_SHORT)
185                     .show();
186         }
187     }
188
189     /**
190      * Notify the owner of a running download that its notification was clicked.
191      */
192     private void sendNotificationClickedIntent(Context context, long[] ids) {
193         final String packageName;
194         final String clazz;
195         final boolean isPublicApi;
196
197         final Uri uri = ContentUris.withAppendedId(
198                 Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, ids[0]);
199         final Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
200         try {
201             if (cursor.moveToFirst()) {
202                 packageName = getString(cursor, Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
203                 clazz = getString(cursor, Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
204                 isPublicApi = getInt(cursor, Downloads.Impl.COLUMN_IS_PUBLIC_API) != 0;
205             } else {
206                 Log.w(TAG, "Missing details for download " + ids[0]);
207                 return;
208             }
209         } finally {
210             cursor.close();
211         }
212
213         if (TextUtils.isEmpty(packageName)) {
214             Log.w(TAG, "Missing package; skipping broadcast");
215             return;
216         }
217
218         Intent appIntent = null;
219         if (isPublicApi) {
220             appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
221             appIntent.setPackage(packageName);
222             appIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, ids);
223
224         } else { // legacy behavior
225             if (TextUtils.isEmpty(clazz)) {
226                 Log.w(TAG, "Missing class; skipping broadcast");
227                 return;
228             }
229
230             appIntent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED);
231             appIntent.setClassName(packageName, clazz);
232             appIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, ids);
233
234             if (ids.length == 1) {
235                 appIntent.setData(uri);
236             } else {
237                 appIntent.setData(Downloads.Impl.CONTENT_URI);
238             }
239         }
240
241         mSystemFacade.sendBroadcast(appIntent);
242     }
243
244     private static String getString(Cursor cursor, String col) {
245         return cursor.getString(cursor.getColumnIndexOrThrow(col));
246     }
247
248     private static int getInt(Cursor cursor, String col) {
249         return cursor.getInt(cursor.getColumnIndexOrThrow(col));
250     }
251
252     private void startService(Context context) {
253         context.startService(new Intent(context, DownloadService.class));
254     }
255 }