blob: f8abc5a66048ae6cd22fa81ad43e411508fe883d [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
Joe Onoratofe4f3ae2010-06-04 11:25:26 -070017package com.android.systemui.usb;
San Mehat64e6a452010-02-04 20:53:48 -080018
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;
Daniel Sandlerc07907e2010-02-22 15:08:41 -050039import android.provider.Settings;
Joe Onorato8a9b2202010-02-26 18:56:32 -080040import android.util.Slog;
San Mehat64e6a452010-02-04 20:53:48 -080041import android.view.View;
42import android.widget.Button;
43import android.widget.ImageView;
44import android.widget.TextView;
45import android.widget.Toast;
46
San Mehatb1043402010-02-05 08:26:50 -080047public class StorageNotification extends StorageEventListener {
San Mehat64e6a452010-02-04 20:53:48 -080048 private static final String TAG = "StorageNotification";
49
Daniel Sandlerc07907e2010-02-22 15:08:41 -050050 private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true;
51
San Mehat64e6a452010-02-04 20:53:48 -080052 /**
53 * Binder context for this service
54 */
55 private Context mContext;
56
57 /**
58 * The notification that is shown when a USB mass storage host
59 * is connected.
60 * <p>
61 * This is lazily created, so use {@link #setUsbStorageNotification()}.
62 */
63 private Notification mUsbStorageNotification;
64
65 /**
66 * The notification that is shown when the following media events occur:
67 * - Media is being checked
68 * - Media is blank (or unknown filesystem)
69 * - Media is corrupt
70 * - Media is safe to unmount
71 * - Media is missing
72 * <p>
73 * This is lazily created, so use {@link #setMediaStorageNotification()}.
74 */
San Mehatb1043402010-02-05 08:26:50 -080075 private Notification mMediaStorageNotification;
76 private boolean mUmsAvailable;
77 private StorageManager mStorageManager;
San Mehat64e6a452010-02-04 20:53:48 -080078
79 public StorageNotification(Context context) {
80 mContext = context;
81
San Mehatb1043402010-02-05 08:26:50 -080082 mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Joe Onoratofe4f3ae2010-06-04 11:25:26 -070083 final boolean connected = mStorageManager.isUsbMassStorageConnected();
Joe Onorato8a9b2202010-02-26 18:56:32 -080084 Slog.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", mUmsAvailable,
San Mehatb1043402010-02-05 08:26:50 -080085 Environment.getExternalStorageState()));
Joe Onoratofe4f3ae2010-06-04 11:25:26 -070086 onUsbMassStorageConnectionChanged(connected);
San Mehat64e6a452010-02-04 20:53:48 -080087 }
88
San Mehatb1043402010-02-05 08:26:50 -080089 /*
90 * @override com.android.os.storage.StorageEventListener
91 */
92 @Override
93 public void onUsbMassStorageConnectionChanged(boolean connected) {
94 mUmsAvailable = connected;
95 /*
96 * Even though we may have a UMS host connected, we the SD card
97 * may not be in a state for export.
98 */
99 String st = Environment.getExternalStorageState();
100
Joe Onorato8a9b2202010-02-26 18:56:32 -0800101 Slog.i(TAG, String.format("UMS connection changed to %s (media state %s)", connected, st));
San Mehatb1043402010-02-05 08:26:50 -0800102
103 if (connected && (st.equals(
104 Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) {
San Mehat64e6a452010-02-04 20:53:48 -0800105 /*
San Mehatb1043402010-02-05 08:26:50 -0800106 * No card or card being checked = don't display
San Mehat64e6a452010-02-04 20:53:48 -0800107 */
San Mehatb1043402010-02-05 08:26:50 -0800108 connected = false;
San Mehat64e6a452010-02-04 20:53:48 -0800109 }
San Mehatb1043402010-02-05 08:26:50 -0800110 updateUsbMassStorageNotification(connected);
San Mehat64e6a452010-02-04 20:53:48 -0800111 }
112
San Mehatb1043402010-02-05 08:26:50 -0800113 /*
114 * @override com.android.os.storage.StorageEventListener
115 */
116 @Override
117 public void onStorageStateChanged(String path, String oldState, String newState) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800118 Slog.i(TAG, String.format(
San Mehatb1043402010-02-05 08:26:50 -0800119 "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
San Mehat64e6a452010-02-04 20:53:48 -0800120 if (newState.equals(Environment.MEDIA_SHARED)) {
San Mehatb1043402010-02-05 08:26:50 -0800121 /*
122 * Storage is now shared. Modify the UMS notification
123 * for stopping UMS.
124 */
San Mehat64e6a452010-02-04 20:53:48 -0800125 Intent intent = new Intent();
Joe Onoratofe4f3ae2010-06-04 11:25:26 -0700126 intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
San Mehat64e6a452010-02-04 20:53:48 -0800127 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
128 setUsbStorageNotification(
129 com.android.internal.R.string.usb_storage_stop_notification_title,
130 com.android.internal.R.string.usb_storage_stop_notification_message,
131 com.android.internal.R.drawable.stat_sys_warning, false, true, pi);
132 } else if (newState.equals(Environment.MEDIA_CHECKING)) {
San Mehatb1043402010-02-05 08:26:50 -0800133 /*
134 * Storage is now checking. Update media notification and disable
135 * UMS notification.
136 */
San Mehat64e6a452010-02-04 20:53:48 -0800137 setMediaStorageNotification(
138 com.android.internal.R.string.ext_media_checking_notification_title,
139 com.android.internal.R.string.ext_media_checking_notification_message,
140 com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null);
141 updateUsbMassStorageNotification(false);
142 } else if (newState.equals(Environment.MEDIA_MOUNTED)) {
San Mehatb1043402010-02-05 08:26:50 -0800143 /*
144 * Storage is now mounted. Dismiss any media notifications,
145 * and enable UMS notification if connected.
146 */
San Mehat64e6a452010-02-04 20:53:48 -0800147 setMediaStorageNotification(0, 0, 0, false, false, null);
148 updateUsbMassStorageNotification(mUmsAvailable);
149 } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) {
San Mehatb1043402010-02-05 08:26:50 -0800150 /*
151 * Storage is now unmounted. We may have been unmounted
152 * because the user is enabling/disabling UMS, in which case we don't
153 * want to display the 'safe to unmount' notification.
154 */
155 if (!mStorageManager.isUsbMassStorageEnabled()) {
156 if (oldState.equals(Environment.MEDIA_SHARED)) {
157 /*
158 * The unmount was due to UMS being enabled. Dismiss any
159 * media notifications, and enable UMS notification if connected
160 */
161 setMediaStorageNotification(0, 0, 0, false, false, null);
162 updateUsbMassStorageNotification(mUmsAvailable);
163 } else {
164 /*
165 * Show safe to unmount media notification, and enable UMS
166 * notification if connected.
167 */
168 setMediaStorageNotification(
169 com.android.internal.R.string.ext_media_safe_unmount_notification_title,
170 com.android.internal.R.string.ext_media_safe_unmount_notification_message,
171 com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
172 updateUsbMassStorageNotification(mUmsAvailable);
173 }
San Mehat64e6a452010-02-04 20:53:48 -0800174 } else {
San Mehatb1043402010-02-05 08:26:50 -0800175 /*
176 * The unmount was due to UMS being enabled. Dismiss any
177 * media notifications, and disable the UMS notification
178 */
San Mehat64e6a452010-02-04 20:53:48 -0800179 setMediaStorageNotification(0, 0, 0, false, false, null);
San Mehatb1043402010-02-05 08:26:50 -0800180 updateUsbMassStorageNotification(false);
San Mehat64e6a452010-02-04 20:53:48 -0800181 }
San Mehat64e6a452010-02-04 20:53:48 -0800182 } else if (newState.equals(Environment.MEDIA_NOFS)) {
San Mehatb1043402010-02-05 08:26:50 -0800183 /*
184 * Storage has no filesystem. Show blank media notification,
185 * and enable UMS notification if connected.
186 */
San Mehat64e6a452010-02-04 20:53:48 -0800187 Intent intent = new Intent();
188 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
189 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
190
191 setMediaStorageNotification(
192 com.android.internal.R.string.ext_media_nofs_notification_title,
193 com.android.internal.R.string.ext_media_nofs_notification_message,
194 com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
195 updateUsbMassStorageNotification(mUmsAvailable);
196 } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) {
San Mehatb1043402010-02-05 08:26:50 -0800197 /*
198 * Storage is corrupt. Show corrupt media notification,
199 * and enable UMS notification if connected.
200 */
San Mehat64e6a452010-02-04 20:53:48 -0800201 Intent intent = new Intent();
202 intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
203 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
204
205 setMediaStorageNotification(
206 com.android.internal.R.string.ext_media_unmountable_notification_title,
207 com.android.internal.R.string.ext_media_unmountable_notification_message,
208 com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
209 updateUsbMassStorageNotification(mUmsAvailable);
San Mehatb1043402010-02-05 08:26:50 -0800210 } else if (newState.equals(Environment.MEDIA_REMOVED)) {
211 /*
212 * Storage has been removed. Show nomedia media notification,
213 * and disable UMS notification regardless of connection state.
214 */
215 setMediaStorageNotification(
216 com.android.internal.R.string.ext_media_nomedia_notification_title,
217 com.android.internal.R.string.ext_media_nomedia_notification_message,
218 com.android.internal.R.drawable.stat_notify_sdcard_usb,
219 true, false, null);
220 updateUsbMassStorageNotification(false);
221 } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) {
222 /*
223 * Storage has been removed unsafely. Show bad removal media notification,
224 * and disable UMS notification regardless of connection state.
225 */
226 setMediaStorageNotification(
227 com.android.internal.R.string.ext_media_badremoval_notification_title,
228 com.android.internal.R.string.ext_media_badremoval_notification_message,
229 com.android.internal.R.drawable.stat_sys_warning,
230 true, true, null);
231 updateUsbMassStorageNotification(false);
232 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800233 Slog.w(TAG, String.format("Ignoring unknown state {%s}", newState));
San Mehat64e6a452010-02-04 20:53:48 -0800234 }
235 }
236
237 /**
238 * Update the state of the USB mass storage notification
239 */
240 void updateUsbMassStorageNotification(boolean available) {
241
242 if (available) {
243 Intent intent = new Intent();
Joe Onoratofe4f3ae2010-06-04 11:25:26 -0700244 intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
San Mehat64e6a452010-02-04 20:53:48 -0800245 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Daniel Sandlerc07907e2010-02-22 15:08:41 -0500246
247 final boolean adbOn = 1 == Settings.Secure.getInt(
248 mContext.getContentResolver(),
249 Settings.Secure.ADB_ENABLED,
250 0);
251
San Mehat64e6a452010-02-04 20:53:48 -0800252 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
253 setUsbStorageNotification(
254 com.android.internal.R.string.usb_storage_notification_title,
255 com.android.internal.R.string.usb_storage_notification_message,
256 com.android.internal.R.drawable.stat_sys_data_usb,
257 false, true, pi);
Daniel Sandlerc07907e2010-02-22 15:08:41 -0500258
259 if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
260 // We assume that developers don't want to enable UMS every
261 // time they attach a device to a USB host. The average user,
262 // however, is looking to charge the phone (in which case this
263 // is harmless) or transfer files (in which case this coaches
264 // the user about how to complete that task and saves several
265 // steps).
266 mContext.startActivity(intent);
267 }
San Mehat64e6a452010-02-04 20:53:48 -0800268 } else {
269 setUsbStorageNotification(0, 0, 0, false, false, null);
270 }
271 }
272
273 /**
274 * Sets the USB storage notification.
275 */
San Mehat4154c072010-02-09 18:37:54 -0800276 private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon,
277 boolean sound, boolean visible, PendingIntent pi) {
San Mehat64e6a452010-02-04 20:53:48 -0800278
279 if (!visible && mUsbStorageNotification == null) {
280 return;
281 }
282
283 NotificationManager notificationManager = (NotificationManager) mContext
284 .getSystemService(Context.NOTIFICATION_SERVICE);
285
286 if (notificationManager == null) {
287 return;
288 }
289
290 if (visible) {
291 Resources r = Resources.getSystem();
292 CharSequence title = r.getText(titleId);
293 CharSequence message = r.getText(messageId);
294
295 if (mUsbStorageNotification == null) {
296 mUsbStorageNotification = new Notification();
297 mUsbStorageNotification.icon = icon;
298 mUsbStorageNotification.when = 0;
299 }
300
301 if (sound) {
302 mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
303 } else {
304 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
305 }
306
307 mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
308
309 mUsbStorageNotification.tickerText = title;
310 if (pi == null) {
311 Intent intent = new Intent();
312 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
313 }
314
315 mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
316 }
317
318 final int notificationId = mUsbStorageNotification.icon;
319 if (visible) {
320 notificationManager.notify(notificationId, mUsbStorageNotification);
321 } else {
322 notificationManager.cancel(notificationId);
323 }
324 }
325
326 private synchronized boolean getMediaStorageNotificationDismissable() {
327 if ((mMediaStorageNotification != null) &&
328 ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
329 Notification.FLAG_AUTO_CANCEL))
330 return true;
331
332 return false;
333 }
334
335 /**
336 * Sets the media storage notification.
337 */
338 private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible,
339 boolean dismissable, PendingIntent pi) {
340
341 if (!visible && mMediaStorageNotification == null) {
342 return;
343 }
344
345 NotificationManager notificationManager = (NotificationManager) mContext
346 .getSystemService(Context.NOTIFICATION_SERVICE);
347
348 if (notificationManager == null) {
349 return;
350 }
351
352 if (mMediaStorageNotification != null && visible) {
353 /*
354 * Dismiss the previous notification - we're about to
355 * re-use it.
356 */
357 final int notificationId = mMediaStorageNotification.icon;
358 notificationManager.cancel(notificationId);
359 }
360
361 if (visible) {
362 Resources r = Resources.getSystem();
363 CharSequence title = r.getText(titleId);
364 CharSequence message = r.getText(messageId);
365
366 if (mMediaStorageNotification == null) {
367 mMediaStorageNotification = new Notification();
368 mMediaStorageNotification.when = 0;
369 }
370
371 mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
372
373 if (dismissable) {
374 mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
375 } else {
376 mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
377 }
378
379 mMediaStorageNotification.tickerText = title;
380 if (pi == null) {
381 Intent intent = new Intent();
382 pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
383 }
384
385 mMediaStorageNotification.icon = icon;
386 mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
387 }
388
389 final int notificationId = mMediaStorageNotification.icon;
390 if (visible) {
391 notificationManager.notify(notificationId, mMediaStorageNotification);
392 } else {
393 notificationManager.cancel(notificationId);
394 }
395 }
396}