summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Sharkey <jsharkey@android.com> 2015-04-15 00:02:10 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2015-04-15 00:02:13 +0000
commit4c08b1bab279fec1a66f1af701ee5c2054eba9ac (patch)
tree9aed0e7b59ead2914945e7c53b865c87597e8ae1
parentdf0db7fe83bfab2926e098a5198f43be299d0632 (diff)
parent56bd3129138b525b0f2eba52bd4fa140f23e792c (diff)
Merge "Checkpoint of storage notifications."
-rw-r--r--core/java/android/os/storage/DiskInfo.java22
-rw-r--r--core/java/android/os/storage/StorageManager.java30
-rw-r--r--core/java/android/os/storage/VolumeInfo.java37
-rw-r--r--core/res/res/values/strings.xml83
-rwxr-xr-xcore/res/res/values/symbols.xml11
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java564
-rw-r--r--services/core/java/com/android/server/MountService.java18
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;
}