diff options
| author | 2015-04-15 00:02:10 +0000 | |
|---|---|---|
| committer | 2015-04-15 00:02:13 +0000 | |
| commit | 4c08b1bab279fec1a66f1af701ee5c2054eba9ac (patch) | |
| tree | 9aed0e7b59ead2914945e7c53b865c87597e8ae1 | |
| parent | df0db7fe83bfab2926e098a5198f43be299d0632 (diff) | |
| parent | 56bd3129138b525b0f2eba52bd4fa140f23e792c (diff) | |
Merge "Checkpoint of storage notifications."
| -rw-r--r-- | core/java/android/os/storage/DiskInfo.java | 22 | ||||
| -rw-r--r-- | core/java/android/os/storage/StorageManager.java | 30 | ||||
| -rw-r--r-- | core/java/android/os/storage/VolumeInfo.java | 37 | ||||
| -rw-r--r-- | core/res/res/values/strings.xml | 83 | ||||
| -rwxr-xr-x | core/res/res/values/symbols.xml | 11 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java | 564 | ||||
| -rw-r--r-- | services/core/java/com/android/server/MountService.java | 18 |
7 files changed, 311 insertions, 454 deletions
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index 4704b67f3355..e6160aac0d1d 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -33,6 +33,8 @@ import java.io.CharArrayWriter; * @hide */ public class DiskInfo implements Parcelable { + public static final String EXTRA_DISK_ID = "android.os.storage.extra.DISK_ID"; + public static final int FLAG_ADOPTABLE = 1 << 0; public static final int FLAG_DEFAULT_PRIMARY = 1 << 1; public static final int FLAG_SD = 1 << 2; @@ -42,7 +44,7 @@ public class DiskInfo implements Parcelable { public final int flags; public long size; public String label; - public String[] volumes; + public String[] volumeIds; public DiskInfo(String id, int flags) { this.id = Preconditions.checkNotNull(id); @@ -54,7 +56,7 @@ public class DiskInfo implements Parcelable { flags = parcel.readInt(); size = parcel.readLong(); label = parcel.readString(); - volumes = parcel.readStringArray(); + volumeIds = parcel.readStringArray(); } public String getDescription() { @@ -68,6 +70,18 @@ public class DiskInfo implements Parcelable { } } + public boolean isSd() { + return (flags & FLAG_SD) != 0; + } + + public boolean isUsb() { + return (flags & FLAG_USB) != 0; + } + + public boolean isAdoptable() { + return (flags & FLAG_ADOPTABLE) != 0; + } + @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); @@ -82,7 +96,7 @@ public class DiskInfo implements Parcelable { pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags)); pw.printPair("size", size); pw.printPair("label", label); - pw.printPair("volumes", volumes); + pw.printPair("volumeIds", volumeIds); pw.decreaseIndent(); pw.println(); } @@ -122,6 +136,6 @@ public class DiskInfo implements Parcelable { parcel.writeInt(this.flags); parcel.writeLong(size); parcel.writeString(label); - parcel.writeStringArray(volumes); + parcel.writeStringArray(volumeIds); } } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index bd42f6a0b0c1..eb774778e0e7 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -35,7 +35,6 @@ import android.util.Log; import android.util.SparseArray; import com.android.internal.os.SomeArgs; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import java.io.File; @@ -456,18 +455,6 @@ public class StorageManager { } /** {@hide} */ - public @Nullable DiskInfo findDiskByVolumeId(String volId) { - Preconditions.checkNotNull(volId); - // TODO; go directly to service to make this faster - for (DiskInfo disk : getDisks()) { - if (ArrayUtils.contains(disk.volumes, volId)) { - return disk; - } - } - return null; - } - - /** {@hide} */ public @Nullable VolumeInfo findVolumeById(String id) { Preconditions.checkNotNull(id); // TODO; go directly to service to make this faster @@ -501,17 +488,14 @@ public class StorageManager { } /** {@hide} */ - public @Nullable String getBestVolumeDescription(String volId) { - String descrip = null; - - final VolumeInfo vol = findVolumeById(volId); - if (vol != null) { - descrip = vol.getDescription(); - } + public @Nullable String getBestVolumeDescription(VolumeInfo vol) { + String descrip = vol.getDescription(); - final DiskInfo disk = findDiskByVolumeId(volId); - if (disk != null && TextUtils.isEmpty(descrip)) { - descrip = disk.getDescription(); + if (vol.diskId != null) { + final DiskInfo disk = findDiskById(vol.diskId); + if (disk != null && TextUtils.isEmpty(descrip)) { + descrip = disk.getDescription(); + } } return descrip; diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index beca8b8718f3..fe1e206a650e 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -22,10 +22,12 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.mtp.MtpStorage; +import android.net.Uri; import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import android.provider.DocumentsContract; import android.text.TextUtils; import android.util.ArrayMap; import android.util.DebugUtils; @@ -45,6 +47,8 @@ import java.io.File; * @hide */ public class VolumeInfo implements Parcelable { + public static final String EXTRA_VOLUME_ID = "android.os.storage.extra.VOLUME_ID"; + /** Stub volume representing internal private storage */ public static final String ID_PRIVATE_INTERNAL = "private"; /** Real volume representing internal emulated storage */ @@ -101,6 +105,7 @@ public class VolumeInfo implements Parcelable { /** Framework state */ public final int mtpIndex; public String nickname; + public String diskId; public VolumeInfo(String id, int type, int mtpIndex) { this.id = Preconditions.checkNotNull(id); @@ -120,6 +125,7 @@ public class VolumeInfo implements Parcelable { path = parcel.readString(); mtpIndex = parcel.readInt(); nickname = parcel.readString(); + diskId = parcel.readString(); } public static @NonNull String getEnvironmentForState(int state) { @@ -228,6 +234,34 @@ public class VolumeInfo implements Parcelable { fsUuid, envState); } + // TODO: avoid this layering violation + private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents"; + private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary"; + + /** + * Build an intent to browse the contents of this volume. Only valid for + * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}. + */ + public Intent buildBrowseIntent() { + final Uri uri; + if (type == VolumeInfo.TYPE_PUBLIC) { + uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid); + } else if (VolumeInfo.ID_EMULATED_INTERNAL.equals(id)) { + uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, + DOCUMENT_ROOT_PRIMARY_EMULATED); + } else if (type == VolumeInfo.TYPE_EMULATED) { + // TODO: build intent once supported + uri = null; + } else { + throw new IllegalArgumentException(); + } + + final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setData(uri); + return intent; + } + @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); @@ -250,6 +284,8 @@ public class VolumeInfo implements Parcelable { pw.println(); pw.printPair("path", path); pw.printPair("mtpIndex", mtpIndex); + pw.printPair("nickname", nickname); + pw.printPair("diskId", diskId); pw.decreaseIndent(); pw.println(); } @@ -296,5 +332,6 @@ public class VolumeInfo implements Parcelable { parcel.writeString(path); parcel.writeInt(mtpIndex); parcel.writeString(nickname); + parcel.writeString(diskId); } } diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 87c50e8a1429..d6886bddc139 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2878,51 +2878,44 @@ <string name="candidates_style"><u>candidates</u></string> <!-- External media notification strings --> - <!-- Shown when external media is being checked [CHAR LIMIT=30] --> - <string name="ext_media_checking_notification_title" product="nosdcard">Preparing USB storage</string> - <!-- Shown when external media is being checked --> - <string name="ext_media_checking_notification_title" product="default">Preparing SD card</string> - <string name="ext_media_checking_notification_message">Checking for errors.</string> - - <!-- Shown when external media is blank (or unsupported filesystem) [CHAR LIMIT=30] --> - <string name="ext_media_nofs_notification_title" product="nosdcard">Blank USB storage</string> - <!-- Shown when external media is blank (or unsupported filesystem) --> - <string name="ext_media_nofs_notification_title" product="default">Blank SD card</string> - <!-- Shown when USB storage cannot be read. [CHAR LIMIT=NONE] --> - <string name="ext_media_nofs_notification_message" product="nosdcard">USB storage is blank or has unsupported filesystem.</string> - <string name="ext_media_nofs_notification_message" product="default">SD card is blank or has unsupported filesystem.</string> - - <!-- Shown when external media is unmountable (corrupt)) [CHAR LIMIT=30] --> - <string name="ext_media_unmountable_notification_title" product="nosdcard">Damaged USB storage</string> - <!-- Shown when external media is unmountable (corrupt)) --> - <string name="ext_media_unmountable_notification_title" product="default">Damaged SD card</string> - <!-- Shown when USB storage cannot be read. [CHAR LIMIT=NONE] --> - <string name="ext_media_unmountable_notification_message" product="nosdcard">USB storage is damaged. Try reformatting it.</string> - <string name="ext_media_unmountable_notification_message" product="default">SD card is damaged. Try reformatting it.</string> - - <!-- Shown when external media is unsafely removed [CHAR LIMIT=30] --> - <string name="ext_media_badremoval_notification_title" product="nosdcard">USB storage unexpectedly removed</string> - <!-- Shown when external media is unsafely removed --> - <string name="ext_media_badremoval_notification_title" product="default">SD card unexpectedly removed</string> - <!-- Shown when external media is unsafely removed. [CHAR LIMIT=NONE] --> - <string name="ext_media_badremoval_notification_message" product="nosdcard">Unmount USB storage before removing to avoid data loss.</string> - <string name="ext_media_badremoval_notification_message" product="default">Unmount SD card before removing to avoid data loss.</string> - - <!-- Shown when external media has been safely removed [CHAR LIMIT=30] --> - <string name="ext_media_safe_unmount_notification_title" product="nosdcard">USB storage safe to remove</string> - <!-- Shown when external media has been safely removed --> - <string name="ext_media_safe_unmount_notification_title" product="default">SD card safe to remove</string> - <!-- Shown when external media has been safely removed. [CHAR LIMIT=NONE] --> - <string name="ext_media_safe_unmount_notification_message" product="nosdcard">You can safely remove USB storage.</string> - <string name="ext_media_safe_unmount_notification_message" product="default">You can safely remove SD card.</string> - - <!-- Shown when external media is missing [CHAR LIMIT=30] --> - <string name="ext_media_nomedia_notification_title" product="nosdcard">Removed USB storage</string> - <!-- Shown when external media is missing --> - <string name="ext_media_nomedia_notification_title" product="default">Removed SD card</string> - <!-- Shown when external media is missing. [CHAR LIMIT=NONE] --> - <string name="ext_media_nomedia_notification_message" product="nosdcard">USB storage removed. Insert new media.</string> - <string name="ext_media_nomedia_notification_message" product="default">SD card removed. Insert a new one.</string> + <skip /> + + <!-- Notification title when external media is being checked [CHAR LIMIT=30] --> + <string name="ext_media_checking_notification_title">Preparing <xliff:g id="name" example="SD card">%s</xliff:g></string> + <!-- Notification body when external media is being checked [CHAR LIMIT=NONE] --> + <string name="ext_media_checking_notification_message">Checking for errors</string> + + <!-- Notification body when new external media is detected [CHAR LIMIT=NONE] --> + <string name="ext_media_new_notification_message">New <xliff:g id="name" example="SD card">%s</xliff:g> detected</string> + <!-- Notification body when external media is ready for use [CHAR LIMIT=NONE] --> + <string name="ext_media_ready_notification_message">For transferring photos and media</string> + + <!-- Notification title when external media is unmountable (corrupt) [CHAR LIMIT=30] --> + <string name="ext_media_unmountable_notification_title">Damaged <xliff:g id="name" example="SD card">%s</xliff:g></string> + <!-- Notification body when external media is unmountable (corrupt) [CHAR LIMIT=NONE] --> + <string name="ext_media_unmountable_notification_message"><xliff:g id="name" example="SD card">%s</xliff:g> is damaged; try reformatting it</string> + + <!-- Notification title when external media is unsafely removed [CHAR LIMIT=30] --> + <string name="ext_media_badremoval_notification_title"><xliff:g id="name" example="SD card">%s</xliff:g> unexpectedly removed</string> + <!-- Notification body when external media is unsafely removed [CHAR LIMIT=NONE] --> + <string name="ext_media_badremoval_notification_message">Unmount <xliff:g id="name" example="SD card">%s</xliff:g> before removing to avoid data loss</string> + + <!-- Notification title when external media is missing [CHAR LIMIT=30] --> + <string name="ext_media_nomedia_notification_title">Removed <xliff:g id="name" example="SD card">%s</xliff:g></string> + <!-- Notification body when external media is missing [CHAR LIMIT=NONE] --> + <string name="ext_media_nomedia_notification_message"><xliff:g id="name" example="SD card">%s</xliff:g> removed; insert a new one</string> + + <!-- Notification title when external media is unmounting [CHAR LIMIT=30] --> + <string name="ext_media_unmounting_notification_title">Still ejecting <xliff:g id="name" example="SD card">%s</xliff:g>\u2026</string> + <!-- Notification body when external media is unmounting [CHAR LIMIT=NONE] --> + <string name="ext_media_unmounting_notification_message">Don\'t remove</string> + + <!-- Notification action to setup external media [CHAR LIMIT=20] --> + <string name="ext_media_init_action">Setup</string> + <!-- Notification action to unmount external media [CHAR LIMIT=20] --> + <string name="ext_media_unmount_action">Eject</string> + <!-- Notification action to browse external media [CHAR LIMIT=20] --> + <string name="ext_media_browse_action">Explore</string> <!-- Shown in LauncherActivity when the requested target Intent didn't return any matching Activities, leaving the list empty. --> <string name="activity_list_empty">No matching activities found.</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 36a736addbf4..fd75d0113b0d 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1886,14 +1886,17 @@ <java-symbol type="string" name="ext_media_badremoval_notification_title" /> <java-symbol type="string" name="ext_media_checking_notification_message" /> <java-symbol type="string" name="ext_media_checking_notification_title" /> - <java-symbol type="string" name="ext_media_nofs_notification_message" /> - <java-symbol type="string" name="ext_media_nofs_notification_title" /> <java-symbol type="string" name="ext_media_nomedia_notification_message" /> <java-symbol type="string" name="ext_media_nomedia_notification_title" /> - <java-symbol type="string" name="ext_media_safe_unmount_notification_message" /> - <java-symbol type="string" name="ext_media_safe_unmount_notification_title" /> <java-symbol type="string" name="ext_media_unmountable_notification_message" /> <java-symbol type="string" name="ext_media_unmountable_notification_title" /> + <java-symbol type="string" name="ext_media_unmounting_notification_message" /> + <java-symbol type="string" name="ext_media_unmounting_notification_title" /> + <java-symbol type="string" name="ext_media_new_notification_message" /> + <java-symbol type="string" name="ext_media_ready_notification_message" /> + <java-symbol type="string" name="ext_media_init_action" /> + <java-symbol type="string" name="ext_media_unmount_action" /> + <java-symbol type="string" name="ext_media_browse_action" /> <java-symbol type="string" name="usb_storage_error_message" /> <java-symbol type="string" name="usb_storage_message" /> <java-symbol type="string" name="usb_storage_notification_message" /> diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index 5771d221ec8f..6830957b96b9 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,424 +17,240 @@ package com.android.systemui.usb; import android.app.Notification; +import android.app.Notification.Action; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; -import android.content.res.Resources; -import android.os.Environment; -import android.os.Handler; -import android.os.HandlerThread; import android.os.UserHandle; +import android.os.storage.DiskInfo; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; -import android.provider.Settings; +import android.os.storage.VolumeInfo; import android.util.Log; +import com.android.internal.R; import com.android.systemui.SystemUI; +import java.util.List; + public class StorageNotification extends SystemUI { private static final String TAG = "StorageNotification"; - private static final boolean DEBUG = false; - - private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true; - - /** - * The notification that is shown when a USB mass storage host - * is connected. - * <p> - * This is lazily created, so use {@link #setUsbStorageNotification()}. - */ - private Notification mUsbStorageNotification; - - /** - * The notification that is shown when the following media events occur: - * - Media is being checked - * - Media is blank (or unknown filesystem) - * - Media is corrupt - * - Media is safe to unmount - * - Media is missing - * <p> - * This is lazily created, so use {@link #setMediaStorageNotification()}. - */ - private Notification mMediaStorageNotification; - private boolean mUmsAvailable; - private StorageManager mStorageManager; - private Handler mAsyncEventHandler; + private static final int NOTIF_ID = 0x53544f52; // STOR - private class StorageNotificationEventListener extends StorageEventListener { - public void onUsbMassStorageConnectionChanged(final boolean connected) { - mAsyncEventHandler.post(new Runnable() { - @Override - public void run() { - onUsbMassStorageConnectionChangedAsync(connected); - } - }); - } - public void onStorageStateChanged(final String path, - final String oldState, final String newState) { - mAsyncEventHandler.post(new Runnable() { - @Override - public void run() { - onStorageStateChangedAsync(path, oldState, newState); - } - }); + // TODO: delay some notifications to avoid bumpy fast operations + // TODO: annoy user when private media is missing + + private NotificationManager mNotificationManager; + private StorageManager mStorageManager; + + private final StorageEventListener mListener = new StorageEventListener() { + @Override + public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { + onVolumeStateChangedInternal(vol, oldState, newState); } - } + }; @Override public void start() { - mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); - final boolean connected = mStorageManager.isUsbMassStorageConnected(); - if (DEBUG) Log.d(TAG, String.format( "Startup with UMS connection %s (media state %s)", - mUmsAvailable, Environment.getExternalStorageState())); - - HandlerThread thr = new HandlerThread("SystemUI StorageNotification"); - thr.start(); - mAsyncEventHandler = new Handler(thr.getLooper()); - - StorageNotificationEventListener listener = new StorageNotificationEventListener(); - listener.onUsbMassStorageConnectionChanged(connected); - mStorageManager.registerListener(listener); - } + mNotificationManager = mContext.getSystemService(NotificationManager.class); - private void onUsbMassStorageConnectionChangedAsync(boolean connected) { - mUmsAvailable = connected; - /* - * Even though we may have a UMS host connected, we the SD card - * may not be in a state for export. - */ - String st = Environment.getExternalStorageState(); - - if (DEBUG) Log.i(TAG, String.format("UMS connection changed to %s (media state %s)", - connected, st)); - - if (connected && (st.equals( - Environment.MEDIA_REMOVED) || st.equals(Environment.MEDIA_CHECKING))) { - /* - * No card or card being checked = don't display - */ - connected = false; - } - updateUsbMassStorageNotification(connected); - } + mStorageManager = mContext.getSystemService(StorageManager.class); + mStorageManager.registerListener(mListener); - private void onStorageStateChangedAsync(String path, String oldState, String newState) { - if (DEBUG) Log.i(TAG, String.format( - "Media {%s} state changed from {%s} -> {%s}", path, oldState, newState)); - if (newState.equals(Environment.MEDIA_SHARED)) { - /* - * Storage is now shared. Modify the UMS notification - * for stopping UMS. - */ - Intent intent = new Intent(); - intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - setUsbStorageNotification( - com.android.internal.R.string.usb_storage_stop_notification_title, - com.android.internal.R.string.usb_storage_stop_notification_message, - com.android.internal.R.drawable.stat_sys_warning, false, true, pi); - } else if (newState.equals(Environment.MEDIA_CHECKING)) { - /* - * Storage is now checking. Update media notification and disable - * UMS notification. - */ - setMediaStorageNotification( - com.android.internal.R.string.ext_media_checking_notification_title, - com.android.internal.R.string.ext_media_checking_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_prepare, true, false, null); - updateUsbMassStorageNotification(false); - } else if (newState.equals(Environment.MEDIA_MOUNTED)) { - /* - * Storage is now mounted. Dismiss any media notifications, - * and enable UMS notification if connected. - */ - setMediaStorageNotification(0, 0, 0, false, false, null); - updateUsbMassStorageNotification(mUmsAvailable); - } else if (newState.equals(Environment.MEDIA_UNMOUNTED)) { - /* - * Storage is now unmounted. We may have been unmounted - * because the user is enabling/disabling UMS, in which case we don't - * want to display the 'safe to unmount' notification. - */ - if (!mStorageManager.isUsbMassStorageEnabled()) { - if (oldState.equals(Environment.MEDIA_SHARED)) { - /* - * The unmount was due to UMS being enabled. Dismiss any - * media notifications, and enable UMS notification if connected - */ - setMediaStorageNotification(0, 0, 0, false, false, null); - updateUsbMassStorageNotification(mUmsAvailable); - } else { - /* - * Show safe to unmount media notification, and enable UMS - * notification if connected. - */ - if (Environment.isExternalStorageRemovable()) { - setMediaStorageNotification( - com.android.internal.R.string.ext_media_safe_unmount_notification_title, - com.android.internal.R.string.ext_media_safe_unmount_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard, true, true, null); - } else { - // This device does not have removable storage, so - // don't tell the user they can remove it. - setMediaStorageNotification(0, 0, 0, false, false, null); - } - updateUsbMassStorageNotification(mUmsAvailable); - } - } else { - /* - * The unmount was due to UMS being enabled. Dismiss any - * media notifications, and disable the UMS notification - */ - setMediaStorageNotification(0, 0, 0, false, false, null); - updateUsbMassStorageNotification(false); - } - } else if (newState.equals(Environment.MEDIA_NOFS)) { - /* - * Storage has no filesystem. Show blank media notification, - * and enable UMS notification if connected. - */ - Intent intent = new Intent(); - intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); - intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, - getVolumeByPath(mStorageManager.getVolumeList(), path)); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - - setMediaStorageNotification( - com.android.internal.R.string.ext_media_nofs_notification_title, - com.android.internal.R.string.ext_media_nofs_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); - updateUsbMassStorageNotification(mUmsAvailable); - } else if (newState.equals(Environment.MEDIA_UNMOUNTABLE)) { - /* - * Storage is corrupt. Show corrupt media notification, - * and enable UMS notification if connected. - */ - Intent intent = new Intent(); - intent.setClass(mContext, com.android.internal.app.ExternalMediaFormatActivity.class); - intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, - getVolumeByPath(mStorageManager.getVolumeList(), path)); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - - setMediaStorageNotification( - com.android.internal.R.string.ext_media_unmountable_notification_title, - com.android.internal.R.string.ext_media_unmountable_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); - updateUsbMassStorageNotification(mUmsAvailable); - } else if (newState.equals(Environment.MEDIA_REMOVED)) { - /* - * Storage has been removed. Show nomedia media notification, - * and disable UMS notification regardless of connection state. - */ - setMediaStorageNotification( - com.android.internal.R.string.ext_media_nomedia_notification_title, - com.android.internal.R.string.ext_media_nomedia_notification_message, - com.android.internal.R.drawable.stat_notify_sdcard_usb, - true, false, null); - updateUsbMassStorageNotification(false); - } else if (newState.equals(Environment.MEDIA_BAD_REMOVAL)) { - /* - * Storage has been removed unsafely. Show bad removal media notification, - * and disable UMS notification regardless of connection state. - */ - setMediaStorageNotification( - com.android.internal.R.string.ext_media_badremoval_notification_title, - com.android.internal.R.string.ext_media_badremoval_notification_message, - com.android.internal.R.drawable.stat_sys_warning, - true, true, null); - updateUsbMassStorageNotification(false); - } else { - Log.w(TAG, String.format("Ignoring unknown state {%s}", newState)); + // Kick current state into place + final List<VolumeInfo> vols = mStorageManager.getVolumes(); + for (VolumeInfo vol : vols) { + onVolumeStateChangedInternal(vol, vol.state, vol.state); } } - /** - * Get the corresponding StorageVolume object for a specific path. - */ - private final StorageVolume getVolumeByPath(StorageVolume[] volumes, String path) { - for (StorageVolume volume : volumes) { - if (volume.getPath().equals(path)) { - return volume; - } + public void onVolumeStateChangedInternal(VolumeInfo vol, int oldState, int newState) { + // We only care about public volumes + if (vol.type != VolumeInfo.TYPE_PUBLIC) { + return; } - Log.w(TAG, "No storage found"); - return null; - } - /** - * Update the state of the USB mass storage notification - */ - void updateUsbMassStorageNotification(boolean available) { - - if (available) { - Intent intent = new Intent(); - intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); - setUsbStorageNotification( - com.android.internal.R.string.usb_storage_notification_title, - com.android.internal.R.string.usb_storage_notification_message, - com.android.internal.R.drawable.stat_sys_data_usb, - false, true, pi); - } else { - setUsbStorageNotification(0, 0, 0, false, false, null); + Log.d(TAG, vol.toString()); + + // New state means we tear down any old notifications + mNotificationManager.cancelAsUser(vol.id, NOTIF_ID, UserHandle.ALL); + + switch (newState) { + case VolumeInfo.STATE_UNMOUNTED: + onVolumeUnmounted(vol); + break; + case VolumeInfo.STATE_MOUNTING: + onVolumeMounting(vol); + break; + case VolumeInfo.STATE_MOUNTED: + onVolumeMounted(vol); + break; + case VolumeInfo.STATE_FORMATTING: + onVolumeFormatting(vol); + break; + case VolumeInfo.STATE_UNMOUNTING: + onVolumeUnmounting(vol); + break; + case VolumeInfo.STATE_UNMOUNTABLE: + onVolumeUnmountable(vol); + break; + case VolumeInfo.STATE_REMOVED: + onVolumeRemoved(vol); + break; } } - /** - * Sets the USB storage notification. - */ - private synchronized void setUsbStorageNotification(int titleId, int messageId, int icon, - boolean sound, boolean visible, PendingIntent pi) { - - if (!visible && mUsbStorageNotification == null) { - return; - } - - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); + private void onVolumeUnmounted(VolumeInfo vol) { + // Ignored + } - if (notificationManager == null) { - return; - } + private void onVolumeMounting(VolumeInfo vol) { + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final CharSequence title = mContext.getString( + R.string.ext_media_checking_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_checking_notification_message, disk.getDescription()); + + final Notification notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard_prepare) + .setCategory(Notification.CATEGORY_PROGRESS) + .setPriority(Notification.PRIORITY_LOW) + .setOngoing(true) + .build(); + + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); + } - if (visible) { - Resources r = Resources.getSystem(); - CharSequence title = r.getText(titleId); - CharSequence message = r.getText(messageId); - - if (mUsbStorageNotification == null) { - mUsbStorageNotification = new Notification(); - mUsbStorageNotification.icon = icon; - mUsbStorageNotification.when = 0; - } - - if (sound) { - mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; - } else { - mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; - } - - mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; - - mUsbStorageNotification.tickerText = title; - if (pi == null) { - Intent intent = new Intent(); - pi = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0, - UserHandle.CURRENT); - } - mUsbStorageNotification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); - mUsbStorageNotification.visibility = Notification.VISIBILITY_PUBLIC; - mUsbStorageNotification.category = Notification.CATEGORY_SYSTEM; - - final boolean adbOn = 1 == Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.ADB_ENABLED, - 0); - - if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) { - // Pop up a full-screen alert to coach the user through enabling UMS. The average - // user has attached the device to USB either to charge the phone (in which case - // this is harmless) or transfer files, and in the latter case this alert saves - // several steps (as well as subtly indicates that you shouldn't mix UMS with other - // activities on the device). - // - // If ADB is enabled, however, we suppress this dialog (under the assumption that a - // developer (a) knows how to enable UMS, and (b) is probably using USB to install - // builds or use adb commands. - mUsbStorageNotification.fullScreenIntent = pi; - } - } + private void onVolumeMounted(VolumeInfo vol) { + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final Notification notif; + if (disk.isAdoptable()) { + final CharSequence title = disk.getDescription(); + final CharSequence text = mContext.getString( + R.string.ext_media_new_notification_message, disk.getDescription()); + + notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .addAction(new Action(0, mContext.getString(R.string.ext_media_init_action), + buildInitPendingIntent(vol))) + .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action), + buildUnmountPendingIntent(vol))) + .setCategory(Notification.CATEGORY_SYSTEM) + .build(); - final int notificationId = mUsbStorageNotification.icon; - if (visible) { - notificationManager.notifyAsUser(null, notificationId, mUsbStorageNotification, - UserHandle.ALL); } else { - notificationManager.cancelAsUser(null, notificationId, UserHandle.ALL); + final CharSequence title = disk.getDescription(); + final CharSequence text = mContext.getString( + R.string.ext_media_ready_notification_message, disk.getDescription()); + + notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .addAction(new Action(0, mContext.getString(R.string.ext_media_browse_action), + buildBrowsePendingIntent(vol))) + .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action), + buildUnmountPendingIntent(vol))) + .setCategory(Notification.CATEGORY_SYSTEM) + .setPriority(Notification.PRIORITY_LOW) + .build(); } + + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); } - private synchronized boolean getMediaStorageNotificationDismissable() { - if ((mMediaStorageNotification != null) && - ((mMediaStorageNotification.flags & Notification.FLAG_AUTO_CANCEL) == - Notification.FLAG_AUTO_CANCEL)) - return true; + private void onVolumeFormatting(VolumeInfo vol) { + // Ignored + } - return false; + private void onVolumeUnmounting(VolumeInfo vol) { + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final CharSequence title = mContext.getString( + R.string.ext_media_unmounting_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_unmounting_notification_message, disk.getDescription()); + + final Notification notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard_prepare) + .setCategory(Notification.CATEGORY_PROGRESS) + .setPriority(Notification.PRIORITY_LOW) + .setOngoing(true) + .build(); + + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); } - /** - * Sets the media storage notification. - */ - private synchronized void setMediaStorageNotification(int titleId, int messageId, int icon, boolean visible, - boolean dismissable, PendingIntent pi) { + private void onVolumeUnmountable(VolumeInfo vol) { + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final CharSequence title = mContext.getString( + R.string.ext_media_unmountable_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_unmountable_notification_message, disk.getDescription()); - if (!visible && mMediaStorageNotification == null) { - return; - } + final Notification notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .setContentIntent(buildDetailsPendingIntent(vol)) + .setCategory(Notification.CATEGORY_ERROR) + .build(); - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); + } - if (notificationManager == null) { + private void onVolumeRemoved(VolumeInfo vol) { + if (!vol.isPrimary()) { + // Ignore non-primary media return; } - if (mMediaStorageNotification != null && visible) { - /* - * Dismiss the previous notification - we're about to - * re-use it. - */ - final int notificationId = mMediaStorageNotification.icon; - notificationManager.cancel(notificationId); - } + final DiskInfo disk = mStorageManager.findDiskById(vol.diskId); + final CharSequence title = mContext.getString( + R.string.ext_media_nomedia_notification_title, disk.getDescription()); + final CharSequence text = mContext.getString( + R.string.ext_media_nomedia_notification_message, disk.getDescription()); - if (visible) { - Resources r = Resources.getSystem(); - CharSequence title = r.getText(titleId); - CharSequence message = r.getText(messageId); - - if (mMediaStorageNotification == null) { - mMediaStorageNotification = new Notification(); - mMediaStorageNotification.when = 0; - } - - mMediaStorageNotification.defaults &= ~Notification.DEFAULT_SOUND; - - if (dismissable) { - mMediaStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; - } else { - mMediaStorageNotification.flags = Notification.FLAG_ONGOING_EVENT; - } - - mMediaStorageNotification.tickerText = title; - if (pi == null) { - Intent intent = new Intent(); - pi = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0, - UserHandle.CURRENT); - } - - mMediaStorageNotification.icon = icon; - mMediaStorageNotification.color = mContext.getColor( - com.android.internal.R.color.system_notification_accent_color); - mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi); - mMediaStorageNotification.visibility = Notification.VISIBILITY_PUBLIC; - mMediaStorageNotification.category = Notification.CATEGORY_SYSTEM; - } + final Notification notif = buildNotificationBuilder(title, text) + .setSmallIcon(R.drawable.stat_notify_sdcard) + .setCategory(Notification.CATEGORY_ERROR) + .build(); - final int notificationId = mMediaStorageNotification.icon; - if (visible) { - notificationManager.notifyAsUser(null, notificationId, - mMediaStorageNotification, UserHandle.ALL); - } else { - notificationManager.cancelAsUser(null, notificationId, UserHandle.ALL); - } + mNotificationManager.notifyAsUser(vol.id, NOTIF_ID, notif, UserHandle.ALL); + } + + private Notification.Builder buildNotificationBuilder(CharSequence title, CharSequence text) { + return new Notification.Builder(mContext) + .setColor(mContext.getColor(R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentText(text) + .setStyle(new Notification.BigTextStyle().bigText(text)) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true); + } + + private PendingIntent buildInitPendingIntent(VolumeInfo vol) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardInit"); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); + return PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); + } + + private PendingIntent buildUnmountPendingIntent(VolumeInfo vol) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageUnmountReceiver"); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); + return PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0, UserHandle.CURRENT); + } + + private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) { + final Intent intent = vol.buildBrowseIntent(); + return PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); + } + + private PendingIntent buildDetailsPendingIntent(VolumeInfo vol) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.Settings$StorageVolumeSettingsActivity"); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); + return PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); } } diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index cac2f5cdfe49..a99f3877f443 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -740,19 +740,29 @@ class MountService extends IMountService.Stub } case VoldResponseCode.DISK_VOLUME_CREATED: { if (cooked.length != 3) break; - final DiskInfo disk = mDisks.get(cooked[1]); + final String diskId = cooked[1]; final String volId = cooked[2]; + final DiskInfo disk = mDisks.get(diskId); if (disk != null) { - disk.volumes = ArrayUtils.appendElement(String.class, disk.volumes, volId); + disk.volumeIds = ArrayUtils.appendElement(String.class, disk.volumeIds, volId); + } + final VolumeInfo vol = mVolumes.get(volId); + if (vol != null) { + vol.diskId = diskId; } break; } case VoldResponseCode.DISK_VOLUME_DESTROYED: { if (cooked.length != 3) break; - final DiskInfo disk = mDisks.get(cooked[1]); + final String diskId = cooked[1]; final String volId = cooked[2]; + final DiskInfo disk = mDisks.get(diskId); if (disk != null) { - disk.volumes = ArrayUtils.removeElement(String.class, disk.volumes, volId); + disk.volumeIds = ArrayUtils.removeElement(String.class, disk.volumeIds, volId); + } + final VolumeInfo vol = mVolumes.get(volId); + if (vol != null) { + vol.diskId = null; } break; } |