/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java

https://github.com/aizuzi/platform_frameworks_base · Java · 414 lines · 260 code · 47 blank · 107 comment · 66 complexity · 3155e72fa8870d9b26e656536d1983fd MD5 · raw file

  1. /*
  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. package com.android.systemui.usb;
  17. import android.app.Notification;
  18. import android.app.NotificationManager;
  19. import android.app.PendingIntent;
  20. import android.content.Context;
  21. import android.content.Intent;
  22. import android.content.res.Resources;
  23. import android.os.Environment;
  24. import android.os.Handler;
  25. import android.os.HandlerThread;
  26. import android.os.UserHandle;
  27. import android.os.storage.StorageEventListener;
  28. import android.os.storage.StorageManager;
  29. import android.provider.Settings;
  30. import android.util.Log;
  31. import com.android.systemui.SystemUI;
  32. public class StorageNotification extends SystemUI {
  33. private static final String TAG = "StorageNotification";
  34. private static final boolean DEBUG = false;
  35. private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true;
  36. /**
  37. * The notification that is shown when a USB mass storage host
  38. * is connected.
  39. * <p>
  40. * This is lazily created, so use {@link #setUsbStorageNotification()}.
  41. */
  42. private Notification mUsbStorageNotification;
  43. /**
  44. * The notification that is shown when the following media events occur:
  45. * - Media is being checked
  46. * - Media is blank (or unknown filesystem)
  47. * - Media is corrupt
  48. * - Media is safe to unmount
  49. * - Media is missing
  50. * <p>
  51. * This is lazily created, so use {@link #setMediaStorageNotification()}.
  52. */
  53. private Notification mMediaStorageNotification;
  54. private boolean mUmsAvailable;
  55. private StorageManager mStorageManager;
  56. private Handler mAsyncEventHandler;
  57. private class StorageNotificationEventListener extends StorageEventListener {
  58. public void onUsbMassStorageConnectionChanged(final boolean connected) {
  59. mAsyncEventHandler.post(new Runnable() {
  60. @Override
  61. public void run() {
  62. onUsbMassStorageConnectionChangedAsync(connected);
  63. }
  64. });
  65. }
  66. public void onStorageStateChanged(final String path,
  67. final String oldState, final String newState) {
  68. mAsyncEventHandler.post(new Runnable() {
  69. @Override
  70. public void run() {
  71. onStorageStateChangedAsync(path, oldState, newState);
  72. }
  73. });
  74. }
  75. }
  76. @Override
  77. public void start() {
  78. mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
  79. final boolean connected = mStorageManager.isUsbMassStorageConnected();
  80. if (DEBUG) Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)",
  81. mUmsAvailable, Environment.getExternalStorageState()));
  82. HandlerThread thr = new HandlerThread("SystemUI StorageNotification");
  83. thr.start();
  84. mAsyncEventHandler = new Handler(thr.getLooper());
  85. StorageNotificationEventListener listener = new StorageNotificationEventListener();
  86. listener.onUsbMassStorageConnectionChanged(connected);
  87. mStorageManager.registerListener(listener);
  88. }
  89. private void onUsbMassStorageConnectionChangedAsync(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. if (DEBUG) Log.i(TAG, String.format("UMS connection changed to %s (media state %s)",
  97. connected, st));
  98. if (connected && (st.equals(
  99. Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) {
  100. /*
  101. * No card or card being checked = don't display
  102. */
  103. connected = false;
  104. }
  105. updateUsbMassStorageNotification(connected);
  106. }
  107. private void onStorageStateChangedAsync(String path, String oldState, String newState) {
  108. if (DEBUG) Log.i(TAG, String.format(
  109. "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState));
  110. if (newState.equals(Environment.MEDIA_SHARED)) {
  111. /*
  112. * Storage is now shared. Modify the UMS notification
  113. * for stopping UMS.
  114. */
  115. Intent intent = new Intent();
  116. intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
  117. PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
  118. setUsbStorageNotification(
  119. com.android.internal.R.string.usb_storage_stop_notification_title,
  120. com.android.internal.R.string.usb_storage_stop_notification_message,
  121. com.android.internal.R.drawable.stat_sys_warning, false, true, pi);
  122. } else if (newState.equals(Environment.MEDIA_CHECKING)) {
  123. /*
  124. * Storage is now checking. Update media notification and disable
  125. * UMS notification.
  126. */
  127. setMediaStorageNotification(
  128. com.android.internal.R.string.ext_media_checking_notification_title,
  129. com.android.internal.R.string.ext_media_checking_notification_message,
  130. com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null);
  131. updateUsbMassStorageNotification(false);
  132. } else if (newState.equals(Environment.MEDIA_MOUNTED)) {
  133. /*
  134. * Storage is now mounted. Dismiss any media notifications,
  135. * and enable UMS notification if connected.
  136. */
  137. setMediaStorageNotification(0, 0, 0, false, false, null);
  138. updateUsbMassStorageNotification(mUmsAvailable);
  139. } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) {
  140. /*
  141. * Storage is now unmounted. We may have been unmounted
  142. * because the user is enabling/disabling UMS, in which case we don't
  143. * want to display the 'safe to unmount' notification.
  144. */
  145. if (!mStorageManager.isUsbMassStorageEnabled()) {
  146. if (oldState.equals(Environment.MEDIA_SHARED)) {
  147. /*
  148. * The unmount was due to UMS being enabled. Dismiss any
  149. * media notifications, and enable UMS notification if connected
  150. */
  151. setMediaStorageNotification(0, 0, 0, false, false, null);
  152. updateUsbMassStorageNotification(mUmsAvailable);
  153. } else {
  154. /*
  155. * Show safe to unmount media notification, and enable UMS
  156. * notification if connected.
  157. */
  158. if (Environment.isExternalStorageRemovable()) {
  159. setMediaStorageNotification(
  160. com.android.internal.R.string.ext_media_safe_unmount_notification_title,
  161. com.android.internal.R.string.ext_media_safe_unmount_notification_message,
  162. com.android.internal.R.drawable.stat_notify_sdcard, true, true, null);
  163. } else {
  164. // This device does not have removable storage, so
  165. // don't tell the user they can remove it.
  166. setMediaStorageNotification(0, 0, 0, false, false, null);
  167. }
  168. updateUsbMassStorageNotification(mUmsAvailable);
  169. }
  170. } else {
  171. /*
  172. * The unmount was due to UMS being enabled. Dismiss any
  173. * media notifications, and disable the UMS notification
  174. */
  175. setMediaStorageNotification(0, 0, 0, false, false, null);
  176. updateUsbMassStorageNotification(false);
  177. }
  178. } else if (newState.equals(Environment.MEDIA_NOFS)) {
  179. /*
  180. * Storage has no filesystem. Show blank media notification,
  181. * and enable UMS notification if connected.
  182. */
  183. Intent intent = new Intent();
  184. intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
  185. PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
  186. setMediaStorageNotification(
  187. com.android.internal.R.string.ext_media_nofs_notification_title,
  188. com.android.internal.R.string.ext_media_nofs_notification_message,
  189. com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
  190. updateUsbMassStorageNotification(mUmsAvailable);
  191. } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) {
  192. /*
  193. * Storage is corrupt. Show corrupt media notification,
  194. * and enable UMS notification if connected.
  195. */
  196. Intent intent = new Intent();
  197. intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class);
  198. PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
  199. setMediaStorageNotification(
  200. com.android.internal.R.string.ext_media_unmountable_notification_title,
  201. com.android.internal.R.string.ext_media_unmountable_notification_message,
  202. com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
  203. updateUsbMassStorageNotification(mUmsAvailable);
  204. } else if (newState.equals(Environment.MEDIA_REMOVED)) {
  205. /*
  206. * Storage has been removed. Show nomedia media notification,
  207. * and disable UMS notification regardless of connection state.
  208. */
  209. setMediaStorageNotification(
  210. com.android.internal.R.string.ext_media_nomedia_notification_title,
  211. com.android.internal.R.string.ext_media_nomedia_notification_message,
  212. com.android.internal.R.drawable.stat_notify_sdcard_usb,
  213. true, false, null);
  214. updateUsbMassStorageNotification(false);
  215. } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) {
  216. /*
  217. * Storage has been removed unsafely. Show bad removal media notification,
  218. * and disable UMS notification regardless of connection state.
  219. */
  220. setMediaStorageNotification(
  221. com.android.internal.R.string.ext_media_badremoval_notification_title,
  222. com.android.internal.R.string.ext_media_badremoval_notification_message,
  223. com.android.internal.R.drawable.stat_sys_warning,
  224. true, true, null);
  225. updateUsbMassStorageNotification(false);
  226. } else {
  227. Log.w(TAG, String.format("Ignoring unknown state {%s}", newState));
  228. }
  229. }
  230. /**
  231. * Update the state of the USB mass storage notification
  232. */
  233. void updateUsbMassStorageNotification(boolean available) {
  234. if (available) {
  235. Intent intent = new Intent();
  236. intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class);
  237. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  238. PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
  239. setUsbStorageNotification(
  240. com.android.internal.R.string.usb_storage_notification_title,
  241. com.android.internal.R.string.usb_storage_notification_message,
  242. com.android.internal.R.drawable.stat_sys_data_usb,
  243. false, true, pi);
  244. } else {
  245. setUsbStorageNotification(0, 0, 0, false, false, null);
  246. }
  247. }
  248. /**
  249. * Sets the USB storage notification.
  250. */
  251. private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon,
  252. boolean sound, boolean visible, PendingIntent pi) {
  253. if (!visible && mUsbStorageNotification == null) {
  254. return;
  255. }
  256. NotificationManager notificationManager = (NotificationManager) mContext
  257. .getSystemService(Context.NOTIFICATION_SERVICE);
  258. if (notificationManager == null) {
  259. return;
  260. }
  261. if (visible) {
  262. Resources r = Resources.getSystem();
  263. CharSequence title = r.getText(titleId);
  264. CharSequence message = r.getText(messageId);
  265. if (mUsbStorageNotification == null) {
  266. mUsbStorageNotification = new Notification();
  267. mUsbStorageNotification.icon = icon;
  268. mUsbStorageNotification.when = 0;
  269. }
  270. if (sound) {
  271. mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND;
  272. } else {
  273. mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
  274. }
  275. mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
  276. mUsbStorageNotification.tickerText = title;
  277. if (pi == null) {
  278. Intent intent = new Intent();
  279. pi = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
  280. UserHandle.CURRENT);
  281. }
  282. mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi);
  283. final boolean adbOn = 1 == Settings.Global.getInt(
  284. mContext.getContentResolver(),
  285. Settings.Global.ADB_ENABLED,
  286. 0);
  287. if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
  288. // Pop up a full-screen alert to coach the user through enabling UMS. The average
  289. // user has attached the device to USB either to charge the phone (in which case
  290. // this is harmless) or transfer files, and in the latter case this alert saves
  291. // several steps (as well as subtly indicates that you shouldn't mix UMS with other
  292. // activities on the device).
  293. //
  294. // If ADB is enabled, however, we suppress this dialog (under the assumption that a
  295. // developer (a) knows how to enable UMS, and (b) is probably using USB to install
  296. // builds or use adb commands.
  297. mUsbStorageNotification.fullScreenIntent = pi;
  298. }
  299. }
  300. final int notificationId = mUsbStorageNotification.icon;
  301. if (visible) {
  302. notificationManager.notifyAsUser(null, notificationId, mUsbStorageNotification,
  303. UserHandle.ALL);
  304. } else {
  305. notificationManager.cancelAsUser(null, notificationId, UserHandle.ALL);
  306. }
  307. }
  308. private synchronized boolean getMediaStorageNotificationDismissable() {
  309. if ((mMediaStorageNotification != null) &&
  310. ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) ==
  311. Notification.FLAG_AUTO_CANCEL))
  312. return true;
  313. return false;
  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. if (!visible && mMediaStorageNotification == null) {
  321. return;
  322. }
  323. NotificationManager notificationManager = (NotificationManager) mContext
  324. .getSystemService(Context.NOTIFICATION_SERVICE);
  325. if (notificationManager == null) {
  326. return;
  327. }
  328. if (mMediaStorageNotification != null && visible) {
  329. /*
  330. * Dismiss the previous notification - we're about to
  331. * re-use it.
  332. */
  333. final int notificationId = mMediaStorageNotification.icon;
  334. notificationManager.cancel(notificationId);
  335. }
  336. if (visible) {
  337. Resources r = Resources.getSystem();
  338. CharSequence title = r.getText(titleId);
  339. CharSequence message = r.getText(messageId);
  340. if (mMediaStorageNotification == null) {
  341. mMediaStorageNotification = new Notification();
  342. mMediaStorageNotification.when = 0;
  343. }
  344. mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
  345. if (dismissable) {
  346. mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL;
  347. } else {
  348. mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
  349. }
  350. mMediaStorageNotification.tickerText = title;
  351. if (pi == null) {
  352. Intent intent = new Intent();
  353. pi = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
  354. UserHandle.CURRENT);
  355. }
  356. mMediaStorageNotification.icon = icon;
  357. mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
  358. }
  359. final int notificationId = mMediaStorageNotification.icon;
  360. if (visible) {
  361. notificationManager.notifyAsUser(null, notificationId,
  362. mMediaStorageNotification, UserHandle.ALL);
  363. } else {
  364. notificationManager.cancelAsUser(null, notificationId, UserHandle.ALL);
  365. }
  366. }
  367. }