Home | History | Annotate | Download | only in usb
      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
    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 
    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 
    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 
    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 
    252     private void onPrivateVolumeStateChangedInternal(VolumeInfo vol) {
    253         Log.d(TAG, "Notifying about private volume: " + vol.toString());
    254 
    255         updateMissingPrivateVolumes();
    256     }
    257 
    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 
    300     private Notification onVolumeUnmounted(VolumeInfo vol) {
    301         // Ignored
    302         return null;
    303     }
    304 
    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 
    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 
    371     private Notification onVolumeFormatting(VolumeInfo vol) {
    372         // Ignored
    373         return null;
    374     }
    375 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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