OSDN Git Service

SystemUI: Restrict persistent USB drive notifications to USB disks
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / usb / StorageNotification.java
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
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
17 package com.android.systemui.usb;
18
19 import android.annotation.NonNull;
20 import android.app.Notification;
21 import android.app.Notification.Action;
22 import android.app.NotificationManager;
23 import android.app.PendingIntent;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.PackageManager;
29 import android.content.pm.PackageManager.MoveCallback;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.UserHandle;
33 import android.os.storage.DiskInfo;
34 import android.os.storage.StorageEventListener;
35 import android.os.storage.StorageManager;
36 import android.os.storage.VolumeInfo;
37 import android.os.storage.VolumeRecord;
38 import android.text.TextUtils;
39 import android.text.format.DateUtils;
40 import android.util.Log;
41 import android.util.SparseArray;
42
43 import com.android.internal.R;
44 import com.android.systemui.SystemUI;
45
46 import java.util.List;
47
48 public class StorageNotification extends SystemUI {
49     private static final String TAG = "StorageNotification";
50
51     private static final int PUBLIC_ID = 0x53505542; // SPUB
52     private static final int PRIVATE_ID = 0x53505256; // SPRV
53     private static final int DISK_ID = 0x5344534b; // SDSK
54     private static final int MOVE_ID = 0x534d4f56; // SMOV
55
56     private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
57     private static final String ACTION_FINISH_WIZARD = "com.android.systemui.action.FINISH_WIZARD";
58
59     // TODO: delay some notifications to avoid bumpy fast operations
60
61     private NotificationManager mNotificationManager;
62     private StorageManager mStorageManager;
63
64     private static class MoveInfo {
65         public int moveId;
66         public Bundle extras;
67         public String packageName;
68         public String label;
69         public String volumeUuid;
70     }
71
72     private final SparseArray<MoveInfo> mMoves = new SparseArray<>();
73
74     private final StorageEventListener mListener = new StorageEventListener() {
75         @Override
76         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
77             onVolumeStateChangedInternal(vol);
78         }
79
80         @Override
81         public void onVolumeRecordChanged(VolumeRecord rec) {
82             // Avoid kicking notifications when getting early metadata before
83             // mounted. If already mounted, we're being kicked because of a
84             // nickname or init'ed change.
85             final VolumeInfo vol = mStorageManager.findVolumeByUuid(rec.getFsUuid());
86             if (vol != null && vol.isMountedReadable()) {
87                 onVolumeStateChangedInternal(vol);
88             }
89         }
90
91         @Override
92         public void onVolumeForgotten(String fsUuid) {
93             // Stop annoying the user
94             mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL);
95         }
96
97         @Override
98         public void onDiskScanned(DiskInfo disk, int volumeCount) {
99             onDiskScannedInternal(disk, volumeCount);
100         }
101
102         @Override
103         public void onDiskDestroyed(DiskInfo disk) {
104             onDiskDestroyedInternal(disk);
105         }
106     };
107
108     private final BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
109         @Override
110         public void onReceive(Context context, Intent intent) {
111             // TODO: kick this onto background thread
112             final String fsUuid = intent.getStringExtra(VolumeRecord.EXTRA_FS_UUID);
113             mStorageManager.setVolumeSnoozed(fsUuid, true);
114         }
115     };
116
117     private final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
118         @Override
119         public void onReceive(Context context, Intent intent) {
120             // When finishing the adoption wizard, clean up any notifications
121             // for moving primary storage
122             mNotificationManager.cancelAsUser(null, MOVE_ID, UserHandle.ALL);
123         }
124     };
125
126     private final MoveCallback mMoveCallback = new MoveCallback() {
127         @Override
128         public void onCreated(int moveId, Bundle extras) {
129             final MoveInfo move = new MoveInfo();
130             move.moveId = moveId;
131             move.extras = extras;
132             if (extras != null) {
133                 move.packageName = extras.getString(Intent.EXTRA_PACKAGE_NAME);
134                 move.label = extras.getString(Intent.EXTRA_TITLE);
135                 move.volumeUuid = extras.getString(VolumeRecord.EXTRA_FS_UUID);
136             }
137             mMoves.put(moveId, move);
138         }
139
140         @Override
141         public void onStatusChanged(int moveId, int status, long estMillis) {
142             final MoveInfo move = mMoves.get(moveId);
143             if (move == null) {
144                 Log.w(TAG, "Ignoring unknown move " + moveId);
145                 return;
146             }
147
148             if (PackageManager.isMoveStatusFinished(status)) {
149                 onMoveFinished(move, status);
150             } else {
151                 onMoveProgress(move, status, estMillis);
152             }
153         }
154     };
155
156     @Override
157     public void start() {
158         mNotificationManager = mContext.getSystemService(NotificationManager.class);
159
160         mStorageManager = mContext.getSystemService(StorageManager.class);
161         mStorageManager.registerListener(mListener);
162
163         mContext.registerReceiver(mSnoozeReceiver, new IntentFilter(ACTION_SNOOZE_VOLUME),
164                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
165         mContext.registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_WIZARD),
166                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
167
168         // Kick current state into place
169         final List<DiskInfo> disks = mStorageManager.getDisks();
170         for (DiskInfo disk : disks) {
171             onDiskScannedInternal(disk, disk.volumeCount);
172         }
173
174         final List<VolumeInfo> vols = mStorageManager.getVolumes();
175         for (VolumeInfo vol : vols) {
176             onVolumeStateChangedInternal(vol);
177         }
178
179         mContext.getPackageManager().registerMoveCallback(mMoveCallback, new Handler());
180
181         updateMissingPrivateVolumes();
182     }
183
184     private void updateMissingPrivateVolumes() {
185         final List<VolumeRecord> recs = mStorageManager.getVolumeRecords();
186         for (VolumeRecord rec : recs) {
187             if (rec.getType() != VolumeInfo.TYPE_PRIVATE) continue;
188
189             final String fsUuid = rec.getFsUuid();
190             final VolumeInfo info = mStorageManager.findVolumeByUuid(fsUuid);
191             if ((info != null && info.isMountedWritable()) || rec.isSnoozed()) {
192                 // Yay, private volume is here, or user snoozed
193                 mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL);
194
195             } else {
196                 // Boo, annoy the user to reinsert the private volume
197                 final CharSequence title = mContext.getString(R.string.ext_media_missing_title,
198                         rec.getNickname());
199                 final CharSequence text = mContext.getString(R.string.ext_media_missing_message);
200
201                 Notification.Builder builder = new Notification.Builder(mContext)
202                         .setSmallIcon(R.drawable.ic_sd_card_48dp)
203                         .setColor(mContext.getColor(R.color.system_notification_accent_color))
204                         .setContentTitle(title)
205                         .setContentText(text)
206                         .setContentIntent(buildForgetPendingIntent(rec))
207                         .setStyle(new Notification.BigTextStyle().bigText(text))
208                         .setVisibility(Notification.VISIBILITY_PUBLIC)
209                         .setLocalOnly(true)
210                         .setCategory(Notification.CATEGORY_SYSTEM)
211                         .setDeleteIntent(buildSnoozeIntent(fsUuid));
212                 SystemUI.overrideNotificationAppName(mContext, builder);
213
214                 mNotificationManager.notifyAsUser(fsUuid, PRIVATE_ID, builder
215                         .build(), UserHandle.ALL);
216             }
217         }
218     }
219
220     private void onDiskScannedInternal(DiskInfo disk, int volumeCount) {
221         if (volumeCount == 0 && disk.size > 0) {
222             // No supported volumes found, give user option to format
223             final CharSequence title = mContext.getString(
224                     R.string.ext_media_unsupported_notification_title, disk.getDescription());
225             final CharSequence text = mContext.getString(
226                     R.string.ext_media_unsupported_notification_message, disk.getDescription());
227
228             Notification.Builder builder = new Notification.Builder(mContext)
229                     .setSmallIcon(getSmallIcon(disk, VolumeInfo.STATE_UNMOUNTABLE))
230                     .setColor(mContext.getColor(R.color.system_notification_accent_color))
231                     .setContentTitle(title)
232                     .setContentText(text)
233                     .setContentIntent(buildInitPendingIntent(disk))
234                     .setStyle(new Notification.BigTextStyle().bigText(text))
235                     .setVisibility(Notification.VISIBILITY_PUBLIC)
236                     .setLocalOnly(true)
237                     .setCategory(Notification.CATEGORY_ERROR);
238             SystemUI.overrideNotificationAppName(mContext, builder);
239
240             mNotificationManager.notifyAsUser(disk.getId(), DISK_ID, builder.build(),
241                     UserHandle.ALL);
242
243         } else {
244             // Yay, we have volumes!
245             mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL);
246         }
247     }
248
249     /**
250      * Remove all notifications for a disk when it goes away.
251      *
252      * @param disk The disk that went away.
253      */
254     private void onDiskDestroyedInternal(@NonNull DiskInfo disk) {
255         mNotificationManager.cancelAsUser(disk.getId(), DISK_ID, UserHandle.ALL);
256     }
257
258     private void onVolumeStateChangedInternal(VolumeInfo vol) {
259         switch (vol.getType()) {
260             case VolumeInfo.TYPE_PRIVATE:
261                 onPrivateVolumeStateChangedInternal(vol);
262                 break;
263             case VolumeInfo.TYPE_PUBLIC:
264                 onPublicVolumeStateChangedInternal(vol);
265                 break;
266         }
267     }
268
269     private void onPrivateVolumeStateChangedInternal(VolumeInfo vol) {
270         Log.d(TAG, "Notifying about private volume: " + vol.toString());
271
272         updateMissingPrivateVolumes();
273     }
274
275     private void onPublicVolumeStateChangedInternal(VolumeInfo vol) {
276         Log.d(TAG, "Notifying about public volume: " + vol.toString());
277
278         final Notification notif;
279         switch (vol.getState()) {
280             case VolumeInfo.STATE_UNMOUNTED:
281                 notif = onVolumeUnmounted(vol);
282                 break;
283             case VolumeInfo.STATE_CHECKING:
284                 notif = onVolumeChecking(vol);
285                 break;
286             case VolumeInfo.STATE_MOUNTED:
287             case VolumeInfo.STATE_MOUNTED_READ_ONLY:
288                 notif = onVolumeMounted(vol);
289                 break;
290             case VolumeInfo.STATE_FORMATTING:
291                 notif = onVolumeFormatting(vol);
292                 break;
293             case VolumeInfo.STATE_EJECTING:
294                 notif = onVolumeEjecting(vol);
295                 break;
296             case VolumeInfo.STATE_UNMOUNTABLE:
297                 notif = onVolumeUnmountable(vol);
298                 break;
299             case VolumeInfo.STATE_REMOVED:
300                 notif = onVolumeRemoved(vol);
301                 break;
302             case VolumeInfo.STATE_BAD_REMOVAL:
303                 notif = onVolumeBadRemoval(vol);
304                 break;
305             default:
306                 notif = null;
307                 break;
308         }
309
310         if (notif != null) {
311             mNotificationManager.notifyAsUser(vol.getId(), PUBLIC_ID, notif, UserHandle.ALL);
312         } else {
313             mNotificationManager.cancelAsUser(vol.getId(), PUBLIC_ID, UserHandle.ALL);
314         }
315     }
316
317     private Notification onVolumeUnmounted(VolumeInfo vol) {
318         // Ignored
319         return null;
320     }
321
322     private Notification onVolumeChecking(VolumeInfo vol) {
323         final DiskInfo disk = vol.getDisk();
324         final CharSequence title = mContext.getString(
325                 R.string.ext_media_checking_notification_title, disk.getDescription());
326         final CharSequence text = mContext.getString(
327                 R.string.ext_media_checking_notification_message, disk.getDescription());
328
329         return buildNotificationBuilder(vol, title, text)
330                 .setCategory(Notification.CATEGORY_PROGRESS)
331                 .setPriority(Notification.PRIORITY_LOW)
332                 .setOngoing(true)
333                 .build();
334     }
335
336     private Notification onVolumeMounted(VolumeInfo vol) {
337         final VolumeRecord rec = mStorageManager.findRecordByUuid(vol.getFsUuid());
338         final DiskInfo disk = vol.getDisk();
339
340         // Don't annoy when user dismissed in past.  (But make sure the disk is adoptable; we
341         // used to allow snoozing non-adoptable disks too.)
342         if (rec.isSnoozed() && disk.isAdoptable()) {
343             return null;
344         }
345
346         if (disk.isAdoptable() && !rec.isInited()) {
347             final CharSequence title = disk.getDescription();
348             final CharSequence text = mContext.getString(
349                     R.string.ext_media_new_notification_message, disk.getDescription());
350
351             final PendingIntent initIntent = buildInitPendingIntent(vol);
352             return buildNotificationBuilder(vol, title, text)
353                     .addAction(new Action(R.drawable.ic_settings_24dp,
354                             mContext.getString(R.string.ext_media_init_action), initIntent))
355                     .addAction(new Action(R.drawable.ic_eject_24dp,
356                             mContext.getString(R.string.ext_media_unmount_action),
357                             buildUnmountPendingIntent(vol)))
358                     .setContentIntent(initIntent)
359                     .setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()))
360                     .setCategory(Notification.CATEGORY_SYSTEM)
361                     .build();
362
363         } else {
364             final CharSequence title = disk.getDescription();
365             final CharSequence text = mContext.getString(
366                     R.string.ext_media_ready_notification_message, disk.getDescription());
367
368             final PendingIntent browseIntent = buildBrowsePendingIntent(vol);
369             final Notification.Builder builder = buildNotificationBuilder(vol, title, text)
370                     .addAction(new Action(R.drawable.ic_folder_24dp,
371                             mContext.getString(R.string.ext_media_browse_action),
372                             browseIntent))
373                     .addAction(new Action(R.drawable.ic_eject_24dp,
374                             mContext.getString(R.string.ext_media_unmount_action),
375                             buildUnmountPendingIntent(vol)))
376                     .setContentIntent(browseIntent)
377                     .setCategory(Notification.CATEGORY_SYSTEM)
378                     .setPriority(Notification.PRIORITY_LOW);
379             // USB disks notification can be persistent
380             if (disk.isUsb()) {
381                 builder.setOngoing(mContext.getResources().getBoolean(
382                         R.bool.config_persistUsbDriveNotification));
383             }
384             // Non-adoptable disks can't be snoozed.
385             if (disk.isAdoptable()) {
386                 builder.setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()));
387             }
388
389             return builder.build();
390         }
391     }
392
393     private Notification onVolumeFormatting(VolumeInfo vol) {
394         // Ignored
395         return null;
396     }
397
398     private Notification onVolumeEjecting(VolumeInfo vol) {
399         final DiskInfo disk = vol.getDisk();
400         final CharSequence title = mContext.getString(
401                 R.string.ext_media_unmounting_notification_title, disk.getDescription());
402         final CharSequence text = mContext.getString(
403                 R.string.ext_media_unmounting_notification_message, disk.getDescription());
404
405         return buildNotificationBuilder(vol, title, text)
406                 .setCategory(Notification.CATEGORY_PROGRESS)
407                 .setPriority(Notification.PRIORITY_LOW)
408                 .setOngoing(true)
409                 .build();
410     }
411
412     private Notification onVolumeUnmountable(VolumeInfo vol) {
413         final DiskInfo disk = vol.getDisk();
414         final CharSequence title = mContext.getString(
415                 R.string.ext_media_unmountable_notification_title, disk.getDescription());
416         final CharSequence text = mContext.getString(
417                 R.string.ext_media_unmountable_notification_message, disk.getDescription());
418
419         return buildNotificationBuilder(vol, title, text)
420                 .setContentIntent(buildInitPendingIntent(vol))
421                 .setCategory(Notification.CATEGORY_ERROR)
422                 .build();
423     }
424
425     private Notification onVolumeRemoved(VolumeInfo vol) {
426         if (!vol.isPrimary()) {
427             // Ignore non-primary media
428             return null;
429         }
430
431         final DiskInfo disk = vol.getDisk();
432         final CharSequence title = mContext.getString(
433                 R.string.ext_media_nomedia_notification_title, disk.getDescription());
434         final CharSequence text = mContext.getString(
435                 R.string.ext_media_nomedia_notification_message, disk.getDescription());
436
437         return buildNotificationBuilder(vol, title, text)
438                 .setCategory(Notification.CATEGORY_ERROR)
439                 .build();
440     }
441
442     private Notification onVolumeBadRemoval(VolumeInfo vol) {
443         if (!vol.isPrimary()) {
444             // Ignore non-primary media
445             return null;
446         }
447
448         final DiskInfo disk = vol.getDisk();
449         final CharSequence title = mContext.getString(
450                 R.string.ext_media_badremoval_notification_title, disk.getDescription());
451         final CharSequence text = mContext.getString(
452                 R.string.ext_media_badremoval_notification_message, disk.getDescription());
453
454         return buildNotificationBuilder(vol, title, text)
455                 .setCategory(Notification.CATEGORY_ERROR)
456                 .build();
457     }
458
459     private void onMoveProgress(MoveInfo move, int status, long estMillis) {
460         final CharSequence title;
461         if (!TextUtils.isEmpty(move.label)) {
462             title = mContext.getString(R.string.ext_media_move_specific_title, move.label);
463         } else {
464             title = mContext.getString(R.string.ext_media_move_title);
465         }
466
467         final CharSequence text;
468         if (estMillis < 0) {
469             text = null;
470         } else {
471             text = DateUtils.formatDuration(estMillis);
472         }
473
474         final PendingIntent intent;
475         if (move.packageName != null) {
476             intent = buildWizardMovePendingIntent(move);
477         } else {
478             intent = buildWizardMigratePendingIntent(move);
479         }
480
481         Notification.Builder builder = new Notification.Builder(mContext)
482                 .setSmallIcon(R.drawable.ic_sd_card_48dp)
483                 .setColor(mContext.getColor(R.color.system_notification_accent_color))
484                 .setContentTitle(title)
485                 .setContentText(text)
486                 .setContentIntent(intent)
487                 .setStyle(new Notification.BigTextStyle().bigText(text))
488                 .setVisibility(Notification.VISIBILITY_PUBLIC)
489                 .setLocalOnly(true)
490                 .setCategory(Notification.CATEGORY_PROGRESS)
491                 .setPriority(Notification.PRIORITY_LOW)
492                 .setProgress(100, status, false)
493                 .setOngoing(true);
494         SystemUI.overrideNotificationAppName(mContext, builder);
495
496         mNotificationManager.notifyAsUser(move.packageName, MOVE_ID,
497                 builder.build(), UserHandle.ALL);
498     }
499
500     private void onMoveFinished(MoveInfo move, int status) {
501         if (move.packageName != null) {
502             // We currently ignore finished app moves; just clear the last
503             // published progress
504             mNotificationManager.cancelAsUser(move.packageName, MOVE_ID, UserHandle.ALL);
505             return;
506         }
507
508         final VolumeInfo privateVol = mContext.getPackageManager().getPrimaryStorageCurrentVolume();
509         final String descrip = mStorageManager.getBestVolumeDescription(privateVol);
510
511         final CharSequence title;
512         final CharSequence text;
513         if (status == PackageManager.MOVE_SUCCEEDED) {
514             title = mContext.getString(R.string.ext_media_move_success_title);
515             text = mContext.getString(R.string.ext_media_move_success_message, descrip);
516         } else {
517             title = mContext.getString(R.string.ext_media_move_failure_title);
518             text = mContext.getString(R.string.ext_media_move_failure_message);
519         }
520
521         // Jump back into the wizard flow if we moved to a real disk
522         final PendingIntent intent;
523         if (privateVol != null && privateVol.getDisk() != null) {
524             intent = buildWizardReadyPendingIntent(privateVol.getDisk());
525         } else if (privateVol != null) {
526             intent = buildVolumeSettingsPendingIntent(privateVol);
527         } else {
528             intent = null;
529         }
530
531         Notification.Builder builder = new Notification.Builder(mContext)
532                 .setSmallIcon(R.drawable.ic_sd_card_48dp)
533                 .setColor(mContext.getColor(R.color.system_notification_accent_color))
534                 .setContentTitle(title)
535                 .setContentText(text)
536                 .setContentIntent(intent)
537                 .setStyle(new Notification.BigTextStyle().bigText(text))
538                 .setVisibility(Notification.VISIBILITY_PUBLIC)
539                 .setLocalOnly(true)
540                 .setCategory(Notification.CATEGORY_SYSTEM)
541                 .setPriority(Notification.PRIORITY_LOW)
542                 .setAutoCancel(true);
543         SystemUI.overrideNotificationAppName(mContext, builder);
544
545         mNotificationManager.notifyAsUser(move.packageName, MOVE_ID, builder.build(),
546                 UserHandle.ALL);
547     }
548
549     private int getSmallIcon(DiskInfo disk, int state) {
550         if (disk.isSd()) {
551             switch (state) {
552                 case VolumeInfo.STATE_CHECKING:
553                 case VolumeInfo.STATE_EJECTING:
554                     return R.drawable.ic_sd_card_48dp;
555                 default:
556                     return R.drawable.ic_sd_card_48dp;
557             }
558         } else if (disk.isUsb()) {
559             return R.drawable.ic_usb_48dp;
560         } else {
561             return R.drawable.ic_sd_card_48dp;
562         }
563     }
564
565     private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title,
566             CharSequence text) {
567         Notification.Builder builder = new Notification.Builder(mContext)
568                 .setSmallIcon(getSmallIcon(vol.getDisk(), vol.getState()))
569                 .setColor(mContext.getColor(R.color.system_notification_accent_color))
570                 .setContentTitle(title)
571                 .setContentText(text)
572                 .setStyle(new Notification.BigTextStyle().bigText(text))
573                 .setVisibility(Notification.VISIBILITY_PUBLIC)
574                 .setLocalOnly(true);
575         overrideNotificationAppName(mContext, builder);
576         return builder;
577     }
578
579     private PendingIntent buildInitPendingIntent(DiskInfo disk) {
580         final Intent intent = new Intent();
581         intent.setClassName("com.android.settings",
582                 "com.android.settings.deviceinfo.StorageWizardInit");
583         intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId());
584
585         final int requestKey = disk.getId().hashCode();
586         return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
587                 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
588     }
589
590     private PendingIntent buildInitPendingIntent(VolumeInfo vol) {
591         final Intent intent = new Intent();
592         intent.setClassName("com.android.settings",
593                 "com.android.settings.deviceinfo.StorageWizardInit");
594         intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
595
596         final int requestKey = vol.getId().hashCode();
597         return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
598                 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
599     }
600
601     private PendingIntent buildUnmountPendingIntent(VolumeInfo vol) {
602         final Intent intent = new Intent();
603         intent.setClassName("com.android.settings",
604                 "com.android.settings.deviceinfo.StorageUnmountReceiver");
605         intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
606
607         final int requestKey = vol.getId().hashCode();
608         return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
609                 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
610     }
611
612     private PendingIntent buildBrowsePendingIntent(VolumeInfo vol) {
613         final Intent intent = vol.buildBrowseIntent();
614
615         final int requestKey = vol.getId().hashCode();
616         return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
617                 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
618     }
619
620     private PendingIntent buildVolumeSettingsPendingIntent(VolumeInfo vol) {
621         final Intent intent = new Intent();
622         switch (vol.getType()) {
623             case VolumeInfo.TYPE_PRIVATE:
624                 intent.setClassName("com.android.settings",
625                         "com.android.settings.Settings$PrivateVolumeSettingsActivity");
626                 break;
627             case VolumeInfo.TYPE_PUBLIC:
628                 intent.setClassName("com.android.settings",
629                         "com.android.settings.Settings$PublicVolumeSettingsActivity");
630                 break;
631             default:
632                 return null;
633         }
634         intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
635
636         final int requestKey = vol.getId().hashCode();
637         return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
638                 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
639     }
640
641     private PendingIntent buildSnoozeIntent(String fsUuid) {
642         final Intent intent = new Intent(ACTION_SNOOZE_VOLUME);
643         intent.putExtra(VolumeRecord.EXTRA_FS_UUID, fsUuid);
644
645         final int requestKey = fsUuid.hashCode();
646         return PendingIntent.getBroadcastAsUser(mContext, requestKey, intent,
647                 PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.CURRENT);
648     }
649
650     private PendingIntent buildForgetPendingIntent(VolumeRecord rec) {
651         final Intent intent = new Intent();
652         intent.setClassName("com.android.settings",
653                 "com.android.settings.Settings$PrivateVolumeForgetActivity");
654         intent.putExtra(VolumeRecord.EXTRA_FS_UUID, rec.getFsUuid());
655
656         final int requestKey = rec.getFsUuid().hashCode();
657         return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
658                 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
659     }
660
661     private PendingIntent buildWizardMigratePendingIntent(MoveInfo move) {
662         final Intent intent = new Intent();
663         intent.setClassName("com.android.settings",
664                 "com.android.settings.deviceinfo.StorageWizardMigrateProgress");
665         intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId);
666
667         final VolumeInfo vol = mStorageManager.findVolumeByQualifiedUuid(move.volumeUuid);
668         if (vol != null) {
669             intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId());
670         }
671         return PendingIntent.getActivityAsUser(mContext, move.moveId, intent,
672                 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
673     }
674
675     private PendingIntent buildWizardMovePendingIntent(MoveInfo move) {
676         final Intent intent = new Intent();
677         intent.setClassName("com.android.settings",
678                 "com.android.settings.deviceinfo.StorageWizardMoveProgress");
679         intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId);
680
681         return PendingIntent.getActivityAsUser(mContext, move.moveId, intent,
682                 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
683     }
684
685     private PendingIntent buildWizardReadyPendingIntent(DiskInfo disk) {
686         final Intent intent = new Intent();
687         intent.setClassName("com.android.settings",
688                 "com.android.settings.deviceinfo.StorageWizardReady");
689         intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId());
690
691         final int requestKey = disk.getId().hashCode();
692         return PendingIntent.getActivityAsUser(mContext, requestKey, intent,
693                 PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
694     }
695 }