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