blob: 887661243d0b5b881020dc0febf34ee02521c52c [file] [log] [blame]
San Mehat64e6a452010-02-04 20:53:48 -08001/*
2 * Copyright (C) 2010 Google Inc.
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
17package com.android.internal.app;
18
19import android.app.Activity;
20import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.PackageManager;
29import android.content.res.Resources;
30import android.os.Bundle;
31import android.os.Environment;
32import android.os.Handler;
San Mehatb1043402010-02-05 08:26:50 -080033import android.os.storage.IMountService;
San Mehat64e6a452010-02-04 20:53:48 -080034import android.os.Message;
San Mehat64e6a452010-02-04 20:53:48 -080035import android.os.ServiceManager;
San Mehatb1043402010-02-05 08:26:50 -080036import android.os.storage.StorageEventListener;
37import android.os.storage.StorageManager;
38import android.os.storage.StorageResultCode;
San Mehat64e6a452010-02-04 20:53:48 -080039import android.util.Log;
40import android.view.View;
41import android.widget.Button;
42import android.widget.ImageView;
43import android.widget.TextView;
44import android.widget.Toast;
45
San Mehatb1043402010-02-05 08:26:50 -080046public class StorageNotification extends StorageEventListener {
San Mehat64e6a452010-02-04 20:53:48 -080047 private static final String TAG = "StorageNotification";
48
49 /**
50 * Binder context for this service
51 */
52 private Context mContext;
53
54 /**
55 * The notification that is shown when a USB mass storage host
56 * is connected.
57 * <p>
58 * This is lazily created, so use {@link #setUsbStorageNotification()}.
59 */
60 private Notification mUsbStorageNotification;
61
62 /**
63 * The notification that is shown when the following media events occur:
64 * - Media is being checked
65 * - Media is blank (or unknown filesystem)
66 * - Media is corrupt
67 * - Media is safe to unmount
68 * - Media is missing
69 * <p>
70 * This is lazily created, so use {@link #setMediaStorageNotification()}.
71 */
San Mehatb1043402010-02-05 08:26:50 -080072 private Notification mMediaStorageNotification;
73 private boolean mUmsAvailable;
74 private StorageManager mStorageManager;
San Mehat64e6a452010-02-04 20:53:48 -080075
76 public StorageNotification(Context context) {
77 mContext = context;
78
San Mehatb1043402010-02-05 08:26:50 -080079 mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
80 mUmsAvailable = mStorageManager.isUsbMassStorageConnected();
81 Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable,
82 Environment.getExternalStorageState()));
San Mehat64e6a452010-02-04 20:53:48 -080083 }
84
San Mehatb1043402010-02-05 08:26:50 -080085 /*
86 * @override com.android.os.storage.StorageEventListener
87 */
88 @Override
89 public void onUsbMassStorageConnectionChanged(boolean connected) {
90 mUmsAvailable = connected;
91 /*
92 * Even though we may have a UMS host connected, we the SD card
93 * may not be in a state for export.
94 */
95 String st = Environment.getExternalStorageState();
96
97 Log.i(TAG, String.format("UMS connection changed to %s (media state %s)", connected, st));
98
99 if (connected && (st.equals(
100 Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) {
San Mehat64e6a452010-02-04 20:53:48 -0800101 /*
San Mehatb1043402010-02-05 08:26:50 -0800102 * No card or card being checked = don't display
San Mehat64e6a452010-02-04 20:53:48 -0800103 */
San Mehatb1043402010-02-05 08:26:50 -0800104 connected = false;
San Mehat64e6a452010-02-04 20:53:48 -0800105 }
San Mehatb1043402010-02-05 08:26:50 -0800106 updateUsbMassStorageNotification(connected);
San Mehat64e6a452010-02-04 20:53:48 -0800107 }
108
San Mehatb1043402010-02-05 08:26:50 -0800109 /*
110 * @override com.android.os.storage.StorageEventListener
111 */
112 @Override
113 public void onStorageStateChanged(String path, String oldState, String newState) {
114 Log.i(TAG, String.format(
115 "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
San Mehat64e6a452010-02-04 20:53:48 -0800116 if (newState.equals(Environment.MEDIA_SHARED)) {
San Mehatb1043402010-02-05 08:26:50 -0800117 /*
118 * Storage is now shared. Modify the UMS notification
119 * for stopping UMS.
120 */
San Mehat64e6a452010-02-04 20:53:48 -0800121 Intent intent = new Intent();
122 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
123 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
124 setUsbStorageNotification(
125 com.android.internal.R.string.usb_storage_stop_notification_title,
126 com.android.internal.R.string.usb_storage_stop_notification_message,
127 com.android.internal.R.drawable.stat_sys_warning, false, true, pi);
128 } else if (newState.equals(Environment.MEDIA_CHECKING)) {
San Mehatb1043402010-02-05 08:26:50 -0800129 /*
130 * Storage is now checking. Update media notification and disable
131 * UMS notification.
132 */
San Mehat64e6a452010-02-04 20:53:48 -0800133 setMediaStorageNotification(
134 com.android.internal.R.string.ext_media_checking_notification_title,
135 com.android.internal.R.string.ext_media_checking_notification_message,
136 com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null);
137 updateUsbMassStorageNotification(false);
138 } else if (newState.equals(Environment.MEDIA_MOUNTED)) {
San Mehatb1043402010-02-05 08:26:50 -0800139 /*
140 * Storage is now mounted. Dismiss any media notifications,
141 * and enable UMS notification if connected.
142 */
San Mehat64e6a452010-02-04 20:53:48 -0800143 setMediaStorageNotification(0, 0, 0, false, false, null);
144 updateUsbMassStorageNotification(mUmsAvailable);
145 } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) {
San Mehatb1043402010-02-05 08:26:50 -0800146 /*
147 * Storage is now unmounted. We may have been unmounted
148 * because the user is enabling/disabling UMS, in which case we don't
149 * want to display the 'safe to unmount' notification.
150 */
151 if (!mStorageManager.isUsbMassStorageEnabled()) {
152 if (oldState.equals(Environment.MEDIA_SHARED)) {
153 /*
154 * The unmount was due to UMS being enabled. Dismiss any
155 * media notifications, and enable UMS notification if connected
156 */
157 setMediaStorageNotification(0, 0, 0, false, false, null);
158 updateUsbMassStorageNotification(mUmsAvailable);
159 } else {
160 /*
161 * Show safe to unmount media notification, and enable UMS
162 * notification if connected.
163 */
164 setMediaStorageNotification(
165 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
166 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
167 com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
168 updateUsbMassStorageNotification(mUmsAvailable);
169 }
San Mehat64e6a452010-02-04 20:53:48 -0800170 } else {
San Mehatb1043402010-02-05 08:26:50 -0800171 /*
172 * The unmount was due to UMS being enabled. Dismiss any
173 * media notifications, and disable the UMS notification
174 */
San Mehat64e6a452010-02-04 20:53:48 -0800175 setMediaStorageNotification(0, 0, 0, false, false, null);
San Mehatb1043402010-02-05 08:26:50 -0800176 updateUsbMassStorageNotification(false);
San Mehat64e6a452010-02-04 20:53:48 -0800177 }
San Mehat64e6a452010-02-04 20:53:48 -0800178 } else if (newState.equals(Environment.MEDIA_NOFS)) {
San Mehatb1043402010-02-05 08:26:50 -0800179 /*
180 * Storage has no filesystem. Show blank media notification,
181 * and enable UMS notification if connected.
182 */
San Mehat64e6a452010-02-04 20:53:48 -0800183 Intent intent = new Intent();
184 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
185 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
186
187 setMediaStorageNotification(
188 com.android.internal.R.string.ext_media_nofs_notification_title,
189 com.android.internal.R.string.ext_media_nofs_notification_message,
190 com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
191 updateUsbMassStorageNotification(mUmsAvailable);
192 } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) {
San Mehatb1043402010-02-05 08:26:50 -0800193 /*
194 * Storage is corrupt. Show corrupt media notification,
195 * and enable UMS notification if connected.
196 */
San Mehat64e6a452010-02-04 20:53:48 -0800197 Intent intent = new Intent();
198 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
199 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
200
201 setMediaStorageNotification(
202 com.android.internal.R.string.ext_media_unmountable_notification_title,
203 com.android.internal.R.string.ext_media_unmountable_notification_message,
204 com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
205 updateUsbMassStorageNotification(mUmsAvailable);
San Mehatb1043402010-02-05 08:26:50 -0800206 } else if (newState.equals(Environment.MEDIA_REMOVED)) {
207 /*
208 * Storage has been removed. Show nomedia media notification,
209 * and disable UMS notification regardless of connection state.
210 */
211 setMediaStorageNotification(
212 com.android.internal.R.string.ext_media_nomedia_notification_title,
213 com.android.internal.R.string.ext_media_nomedia_notification_message,
214 com.android.internal.R.drawable.stat_notify_sdcard_usb,
215 true, false, null);
216 updateUsbMassStorageNotification(false);
217 } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) {
218 /*
219 * Storage has been removed unsafely. Show bad removal media notification,
220 * and disable UMS notification regardless of connection state.
221 */
222 setMediaStorageNotification(
223 com.android.internal.R.string.ext_media_badremoval_notification_title,
224 com.android.internal.R.string.ext_media_badremoval_notification_message,
225 com.android.internal.R.drawable.stat_sys_warning,
226 true, true, null);
227 updateUsbMassStorageNotification(false);
228 } else {
229 Log.w(TAG, String.format("Ignoring unknown state {%s}", newState));
San Mehat64e6a452010-02-04 20:53:48 -0800230 }
231 }
232
233 /**
234 * Update the state of the USB mass storage notification
235 */
236 void updateUsbMassStorageNotification(boolean available) {
237
238 if (available) {
239 Intent intent = new Intent();
240 intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
241 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
242 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
243 setUsbStorageNotification(
244 com.android.internal.R.string.usb_storage_notification_title,
245 com.android.internal.R.string.usb_storage_notification_message,
246 com.android.internal.R.drawable.stat_sys_data_usb,
247 false, true, pi);
248 } else {
249 setUsbStorageNotification(0, 0, 0, false, false, null);
250 }
251 }
252
253 /**
254 * Sets the USB storage notification.
255 */
256 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, boolean sound, boolean visible,
257 PendingIntent pi) {
258
259 if (!visible && mUsbStorageNotification == null) {
260 return;
261 }
262
263 NotificationManager notificationManager = (NotificationManager) mContext
264 .getSystemService(Context.NOTIFICATION_SERVICE);
265
266 if (notificationManager == null) {
267 return;
268 }
269
270 if (visible) {
271 Resources r = Resources.getSystem();
272 CharSequence title = r.getText(titleId);
273 CharSequence message = r.getText(messageId);
274
275 if (mUsbStorageNotification == null) {
276 mUsbStorageNotification = new Notification();
277 mUsbStorageNotification.icon = icon;
278 mUsbStorageNotification.when = 0;
279 }
280
281 if (sound) {
282 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
283 } else {
284 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
285 }
286
287 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
288
289 mUsbStorageNotification.tickerText = title;
290 if (pi == null) {
291 Intent intent = new Intent();
292 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
293 }
294
295 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
296 }
297
298 final int notificationId = mUsbStorageNotification.icon;
299 if (visible) {
300 notificationManager.notify(notificationId, mUsbStorageNotification);
301 } else {
302 notificationManager.cancel(notificationId);
303 }
304 }
305
306 private synchronized boolean getMediaStorageNotificationDismissable() {
307 if ((mMediaStorageNotification != null) &&
308 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
309 Notification.FLAG_AUTO_CANCEL))
310 return true;
311
312 return false;
313 }
314
315 /**
316 * Sets the media storage notification.
317 */
318 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
319 boolean dismissable, PendingIntent pi) {
320
321 if (!visible && mMediaStorageNotification == null) {
322 return;
323 }
324
325 NotificationManager notificationManager = (NotificationManager) mContext
326 .getSystemService(Context.NOTIFICATION_SERVICE);
327
328 if (notificationManager == null) {
329 return;
330 }
331
332 if (mMediaStorageNotification != null && visible) {
333 /*
334 * Dismiss the previous notification - we're about to
335 * re-use it.
336 */
337 final int notificationId = mMediaStorageNotification.icon;
338 notificationManager.cancel(notificationId);
339 }
340
341 if (visible) {
342 Resources r = Resources.getSystem();
343 CharSequence title = r.getText(titleId);
344 CharSequence message = r.getText(messageId);
345
346 if (mMediaStorageNotification == null) {
347 mMediaStorageNotification = new Notification();
348 mMediaStorageNotification.when = 0;
349 }
350
351 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
352
353 if (dismissable) {
354 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
355 } else {
356 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
357 }
358
359 mMediaStorageNotification.tickerText = title;
360 if (pi == null) {
361 Intent intent = new Intent();
362 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
363 }
364
365 mMediaStorageNotification.icon = icon;
366 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
367 }
368
369 final int notificationId = mMediaStorageNotification.icon;
370 if (visible) {
371 notificationManager.notify(notificationId, mMediaStorageNotification);
372 } else {
373 notificationManager.cancel(notificationId);
374 }
375 }
376}