Home | History | Annotate | Download | only in deviceinfo
      1 /*
      2  * Copyright (C) 2008 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.settings.deviceinfo;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.content.BroadcastReceiver;
     23 import android.content.Context;
     24 import android.content.DialogInterface;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.DialogInterface.OnCancelListener;
     28 import android.content.pm.ApplicationInfo;
     29 import android.content.res.Resources;
     30 import android.os.Bundle;
     31 import android.os.IBinder;
     32 import android.os.RemoteException;
     33 import android.os.Environment;
     34 import android.os.storage.IMountService;
     35 import android.os.ServiceManager;
     36 import android.os.StatFs;
     37 import android.os.storage.StorageManager;
     38 import android.os.storage.StorageEventListener;
     39 import android.preference.Preference;
     40 import android.preference.PreferenceActivity;
     41 import android.preference.PreferenceGroup;
     42 import android.preference.PreferenceScreen;
     43 import android.text.format.Formatter;
     44 import android.util.Log;
     45 import android.widget.Toast;
     46 
     47 import com.android.settings.R;
     48 
     49 import java.io.File;
     50 import java.util.List;
     51 
     52 public class Memory extends PreferenceActivity implements OnCancelListener {
     53     private static final String TAG = "Memory";
     54     private static final boolean localLOGV = false;
     55 
     56     private static final String MEMORY_SD_SIZE = "memory_sd_size";
     57 
     58     private static final String MEMORY_SD_AVAIL = "memory_sd_avail";
     59 
     60     private static final String MEMORY_SD_MOUNT_TOGGLE = "memory_sd_mount_toggle";
     61 
     62     private static final String MEMORY_SD_FORMAT = "memory_sd_format";
     63 
     64     private static final String MEMORY_SD_GROUP = "memory_sd";
     65 
     66     private static final int DLG_CONFIRM_UNMOUNT = 1;
     67     private static final int DLG_ERROR_UNMOUNT = 2;
     68 
     69     private Resources mRes;
     70 
     71     private Preference mSdSize;
     72     private Preference mSdAvail;
     73     private Preference mSdMountToggle;
     74     private Preference mSdFormat;
     75     private PreferenceGroup mSdMountPreferenceGroup;
     76 
     77     boolean mSdMountToggleAdded = true;
     78 
     79     // Access using getMountService()
     80     private IMountService mMountService = null;
     81 
     82     private StorageManager mStorageManager = null;
     83 
     84     @Override
     85     protected void onCreate(Bundle icicle) {
     86         super.onCreate(icicle);
     87 
     88         if (mStorageManager == null) {
     89             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
     90             mStorageManager.registerListener(mStorageListener);
     91         }
     92 
     93         addPreferencesFromResource(R.xml.device_info_memory);
     94 
     95         mRes = getResources();
     96         mSdSize = findPreference(MEMORY_SD_SIZE);
     97         mSdAvail = findPreference(MEMORY_SD_AVAIL);
     98         mSdMountToggle = findPreference(MEMORY_SD_MOUNT_TOGGLE);
     99         mSdFormat = findPreference(MEMORY_SD_FORMAT);
    100 
    101         mSdMountPreferenceGroup = (PreferenceGroup)findPreference(MEMORY_SD_GROUP);
    102     }
    103 
    104     @Override
    105     protected void onResume() {
    106         super.onResume();
    107 
    108         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
    109         intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
    110         intentFilter.addDataScheme("file");
    111         registerReceiver(mReceiver, intentFilter);
    112 
    113         updateMemoryStatus();
    114     }
    115 
    116     StorageEventListener mStorageListener = new StorageEventListener() {
    117 
    118         @Override
    119         public void onStorageStateChanged(String path, String oldState, String newState) {
    120             Log.i(TAG, "Received storage state changed notification that " +
    121                     path + " changed state from " + oldState +
    122                     " to " + newState);
    123             updateMemoryStatus();
    124         }
    125     };
    126 
    127     @Override
    128     protected void onPause() {
    129         super.onPause();
    130         unregisterReceiver(mReceiver);
    131     }
    132 
    133     @Override
    134     protected void onDestroy() {
    135         if (mStorageManager != null && mStorageListener != null) {
    136             mStorageManager.unregisterListener(mStorageListener);
    137         }
    138         super.onDestroy();
    139     }
    140 
    141     private synchronized IMountService getMountService() {
    142        if (mMountService == null) {
    143            IBinder service = ServiceManager.getService("mount");
    144            if (service != null) {
    145                mMountService = IMountService.Stub.asInterface(service);
    146            } else {
    147                Log.e(TAG, "Can't get mount service");
    148            }
    149        }
    150        return mMountService;
    151     }
    152 
    153     @Override
    154     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    155         if (preference == mSdMountToggle) {
    156             String status = Environment.getExternalStorageState();
    157             if (status.equals(Environment.MEDIA_MOUNTED)) {
    158                 unmount();
    159             } else {
    160                 mount();
    161             }
    162             return true;
    163         } else if (preference == mSdFormat) {
    164             Intent intent = new Intent(Intent.ACTION_VIEW);
    165             intent.setClass(this, com.android.settings.MediaFormat.class);
    166             startActivity(intent);
    167             return true;
    168         }
    169 
    170         return false;
    171     }
    172 
    173     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    174         @Override
    175         public void onReceive(Context context, Intent intent) {
    176             updateMemoryStatus();
    177         }
    178     };
    179 
    180     @Override
    181     public Dialog onCreateDialog(int id, Bundle args) {
    182         switch (id) {
    183         case DLG_CONFIRM_UNMOUNT:
    184             return new AlertDialog.Builder(this)
    185                     .setTitle(R.string.dlg_confirm_unmount_title)
    186                     .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
    187                         public void onClick(DialogInterface dialog, int which) {
    188                             doUnmount(true);
    189                         }})
    190                     .setNegativeButton(R.string.cancel, null)
    191                     .setMessage(R.string.dlg_confirm_unmount_text)
    192                     .setOnCancelListener(this)
    193                     .create();
    194         case DLG_ERROR_UNMOUNT:
    195             return new AlertDialog.Builder(this                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            )
    196             .setTitle(R.string.dlg_error_unmount_title)
    197             .setNeutralButton(R.string.dlg_ok, null)
    198             .setMessage(R.string.dlg_error_unmount_text)
    199             .setOnCancelListener(this)
    200             .create();
    201         }
    202         return null;
    203     }
    204 
    205     private void doUnmount(boolean force) {
    206         // Present a toast here
    207         Toast.makeText(this, R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
    208         IMountService mountService = getMountService();
    209         String extStoragePath = Environment.getExternalStorageDirectory().toString();
    210         try {
    211             mSdMountToggle.setEnabled(false);
    212             mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
    213             mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
    214             mountService.unmountVolume(extStoragePath, force);
    215         } catch (RemoteException e) {
    216             // Informative dialog to user that
    217             // unmount failed.
    218             showDialogInner(DLG_ERROR_UNMOUNT);
    219         }
    220     }
    221 
    222     private void showDialogInner(int id) {
    223         removeDialog(id);
    224         showDialog(id);
    225     }
    226 
    227     private boolean hasAppsAccessingStorage() throws RemoteException {
    228         String extStoragePath = Environment.getExternalStorageDirectory().toString();
    229         IMountService mountService = getMountService();
    230         int stUsers[] = mountService.getStorageUsers(extStoragePath);
    231         if (stUsers != null && stUsers.length > 0) {
    232             return true;
    233         }
    234         ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
    235         List<ApplicationInfo> list = am.getRunningExternalApplications();
    236         if (list != null && list.size() > 0) {
    237             return true;
    238         }
    239         return false;
    240     }
    241 
    242     private void unmount() {
    243         // Check if external media is in use.
    244         try {
    245            if (hasAppsAccessingStorage()) {
    246                if (localLOGV) Log.i(TAG, "Do have storage users accessing media");
    247                // Present dialog to user
    248                showDialogInner(DLG_CONFIRM_UNMOUNT);
    249            } else {
    250                doUnmount(true);
    251            }
    252         } catch (RemoteException e) {
    253             // Very unlikely. But present an error dialog anyway
    254             Log.e(TAG, "Is MountService running?");
    255             showDialogInner(DLG_ERROR_UNMOUNT);
    256         }
    257     }
    258 
    259     private void mount() {
    260         IMountService mountService = getMountService();
    261         try {
    262             if (mountService != null) {
    263                 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
    264             } else {
    265                 Log.e(TAG, "Mount service is null, can't mount");
    266             }
    267         } catch (RemoteException ex) {
    268         }
    269     }
    270 
    271     private void updateMemoryStatus() {
    272         String status = Environment.getExternalStorageState();
    273         String readOnly = "";
    274         if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
    275             status = Environment.MEDIA_MOUNTED;
    276             readOnly = mRes.getString(R.string.read_only);
    277         }
    278 
    279         if (status.equals(Environment.MEDIA_MOUNTED)) {
    280             if (!Environment.isExternalStorageRemovable()) {
    281                 // This device has built-in storage that is not removable.
    282                 // There is no reason for the user to unmount it.
    283                 if (mSdMountToggleAdded) {
    284                     mSdMountPreferenceGroup.removePreference(mSdMountToggle);
    285                     mSdMountToggleAdded = false;
    286                 }
    287             }
    288             try {
    289                 File path = Environment.getExternalStorageDirectory();
    290                 StatFs stat = new StatFs(path.getPath());
    291                 long blockSize = stat.getBlockSize();
    292                 long totalBlocks = stat.getBlockCount();
    293                 long availableBlocks = stat.getAvailableBlocks();
    294 
    295                 mSdSize.setSummary(formatSize(totalBlocks * blockSize));
    296                 mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);
    297 
    298                 mSdMountToggle.setEnabled(true);
    299                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
    300                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
    301 
    302             } catch (IllegalArgumentException e) {
    303                 // this can occur if the SD card is removed, but we haven't received the
    304                 // ACTION_MEDIA_REMOVED Intent yet.
    305                 status = Environment.MEDIA_REMOVED;
    306             }
    307 
    308         } else {
    309             mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
    310             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
    311 
    312 
    313             if (!Environment.isExternalStorageRemovable()) {
    314                 if (status.equals(Environment.MEDIA_UNMOUNTED)) {
    315                     if (!mSdMountToggleAdded) {
    316                         mSdMountPreferenceGroup.addPreference(mSdMountToggle);
    317                         mSdMountToggleAdded = true;
    318                     }
    319                 }
    320             }
    321 
    322             if (status.equals(Environment.MEDIA_UNMOUNTED) ||
    323                 status.equals(Environment.MEDIA_NOFS) ||
    324                 status.equals(Environment.MEDIA_UNMOUNTABLE) ) {
    325                 mSdMountToggle.setEnabled(true);
    326                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
    327                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
    328             } else {
    329                 mSdMountToggle.setEnabled(false);
    330                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
    331                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
    332             }
    333         }
    334 
    335         File path = Environment.getDataDirectory();
    336         StatFs stat = new StatFs(path.getPath());
    337         long blockSize = stat.getBlockSize();
    338         long availableBlocks = stat.getAvailableBlocks();
    339         findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks * blockSize));
    340     }
    341 
    342     private String formatSize(long size) {
    343         return Formatter.formatFileSize(this, size);
    344     }
    345 
    346     public void onCancel(DialogInterface dialog) {
    347         finish();
    348     }
    349 
    350 }
    351