2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.deviceinfo;
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;
47 import com.android.settings.R;
50 import java.util.List;
52 public class Memory extends PreferenceActivity implements OnCancelListener {
53 private static final String TAG = "Memory";
54 private static final boolean localLOGV = false;
56 private static final String MEMORY_SD_SIZE = "memory_sd_size";
58 private static final String MEMORY_SD_AVAIL = "memory_sd_avail";
60 private static final String MEMORY_SD_MOUNT_TOGGLE = "memory_sd_mount_toggle";
62 private static final String MEMORY_SD_FORMAT = "memory_sd_format";
64 private static final String MEMORY_SD_GROUP = "memory_sd";
66 private static final int DLG_CONFIRM_UNMOUNT = 1;
67 private static final int DLG_ERROR_UNMOUNT = 2;
69 private Resources mRes;
71 private Preference mSdSize;
72 private Preference mSdAvail;
73 private Preference mSdMountToggle;
74 private Preference mSdFormat;
75 private PreferenceGroup mSdMountPreferenceGroup;
77 boolean mSdMountToggleAdded = true;
79 // Access using getMountService()
80 private IMountService mMountService = null;
82 private StorageManager mStorageManager = null;
85 protected void onCreate(Bundle icicle) {
86 super.onCreate(icicle);
88 if (mStorageManager == null) {
89 mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
90 mStorageManager.registerListener(mStorageListener);
93 addPreferencesFromResource(R.xml.device_info_memory);
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);
101 mSdMountPreferenceGroup = (PreferenceGroup)findPreference(MEMORY_SD_GROUP);
105 protected void onResume() {
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);
113 updateMemoryStatus();
116 StorageEventListener mStorageListener = new StorageEventListener() {
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 +
123 updateMemoryStatus();
128 protected void onPause() {
130 unregisterReceiver(mReceiver);
134 protected void onDestroy() {
135 if (mStorageManager != null && mStorageListener != null) {
136 mStorageManager.unregisterListener(mStorageListener);
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);
147 Log.e(TAG, "Can't get mount service");
150 return mMountService;
154 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
155 if (preference == mSdMountToggle) {
156 String status = Environment.getExternalStorageState();
157 if (status.equals(Environment.MEDIA_MOUNTED)) {
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);
173 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
175 public void onReceive(Context context, Intent intent) {
176 updateMemoryStatus();
181 public Dialog onCreateDialog(int id, Bundle args) {
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) {
190 .setNegativeButton(R.string.cancel, null)
191 .setMessage(R.string.dlg_confirm_unmount_text)
192 .setOnCancelListener(this)
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)
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();
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
218 showDialogInner(DLG_ERROR_UNMOUNT);
222 private void showDialogInner(int id) {
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) {
234 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
235 List<ApplicationInfo> list = am.getRunningExternalApplications();
236 if (list != null && list.size() > 0) {
242 private void unmount() {
243 // Check if external media is in use.
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);
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);
259 private void mount() {
260 IMountService mountService = getMountService();
262 if (mountService != null) {
263 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
265 Log.e(TAG, "Mount service is null, can't mount");
267 } catch (RemoteException ex) {
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);
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;
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();
295 mSdSize.setSummary(formatSize(totalBlocks * blockSize));
296 mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);
298 mSdMountToggle.setEnabled(true);
299 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
300 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
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;
309 mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
310 mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
313 if (!Environment.isExternalStorageRemovable()) {
314 if (status.equals(Environment.MEDIA_UNMOUNTED)) {
315 if (!mSdMountToggleAdded) {
316 mSdMountPreferenceGroup.addPreference(mSdMountToggle);
317 mSdMountToggleAdded = true;
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));
329 mSdMountToggle.setEnabled(false);
330 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
331 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
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));
342 private String formatSize(long size) {
343 return Formatter.formatFileSize(this, size);
346 public void onCancel(DialogInterface dialog) {