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.pm.PackageManager;
30 import android.content.pm.PackageManager.NameNotFoundException;
31 import android.content.res.Resources;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.os.Message;
36 import android.os.RemoteException;
37 import android.os.Environment;
38 import android.os.storage.IMountService;
39 import android.os.ServiceManager;
40 import android.os.StatFs;
41 import android.os.storage.StorageManager;
42 import android.os.storage.StorageEventListener;
43 import android.preference.Preference;
44 import android.preference.PreferenceActivity;
45 import android.preference.PreferenceScreen;
46 import android.text.format.Formatter;
47 import android.util.Log;
48 import android.widget.Toast;
50 import com.android.settings.R;
53 import java.util.HashSet;
54 import java.util.List;
57 public class Memory extends PreferenceActivity implements OnCancelListener {
58 private static final String TAG = "Memory";
59 private static final boolean localLOGV = false;
61 private static final String MEMORY_SD_SIZE = "memory_sd_size";
63 private static final String MEMORY_SD_AVAIL = "memory_sd_avail";
65 private static final String MEMORY_SD_MOUNT_TOGGLE = "memory_sd_mount_toggle";
67 private static final String MEMORY_SD_FORMAT = "memory_sd_format";
69 private static final int DLG_CONFIRM_UNMOUNT = 1;
70 private static final int DLG_ERROR_UNMOUNT = 2;
72 private Resources mRes;
74 private Preference mSdSize;
75 private Preference mSdAvail;
76 private Preference mSdMountToggle;
77 private Preference mSdFormat;
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);
103 protected void onResume() {
106 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
107 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
108 intentFilter.addDataScheme("file");
109 registerReceiver(mReceiver, intentFilter);
111 updateMemoryStatus();
114 StorageEventListener mStorageListener = new StorageEventListener() {
117 public void onStorageStateChanged(String path, String oldState, String newState) {
118 Log.i(TAG, "Received storage state changed notification that " +
119 path + " changed state from " + oldState +
121 updateMemoryStatus();
126 protected void onPause() {
128 unregisterReceiver(mReceiver);
132 protected void onDestroy() {
133 if (mStorageManager != null && mStorageListener != null) {
134 mStorageManager.unregisterListener(mStorageListener);
139 private synchronized IMountService getMountService() {
140 if (mMountService == null) {
141 IBinder service = ServiceManager.getService("mount");
142 if (service != null) {
143 mMountService = IMountService.Stub.asInterface(service);
145 Log.e(TAG, "Can't get mount service");
148 return mMountService;
152 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
153 if (preference == mSdMountToggle) {
154 String status = Environment.getExternalStorageState();
155 if (status.equals(Environment.MEDIA_MOUNTED)) {
161 } else if (preference == mSdFormat) {
162 Intent intent = new Intent(Intent.ACTION_VIEW);
163 intent.setClass(this, com.android.settings.MediaFormat.class);
164 startActivity(intent);
171 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
173 public void onReceive(Context context, Intent intent) {
174 updateMemoryStatus();
179 public Dialog onCreateDialog(int id, Bundle args) {
181 case DLG_CONFIRM_UNMOUNT:
182 return new AlertDialog.Builder(this)
183 .setTitle(R.string.dlg_confirm_unmount_title)
184 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
185 public void onClick(DialogInterface dialog, int which) {
188 .setNegativeButton(R.string.cancel, null)
189 .setMessage(R.string.dlg_confirm_unmount_text)
190 .setOnCancelListener(this)
192 case DLG_ERROR_UNMOUNT:
193 return new AlertDialog.Builder(this )
194 .setTitle(R.string.dlg_error_unmount_title)
195 .setNeutralButton(R.string.dlg_ok, null)
196 .setMessage(R.string.dlg_error_unmount_text)
197 .setOnCancelListener(this)
203 private void doUnmount(boolean force) {
204 // Present a toast here
205 Toast.makeText(this, R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
206 IMountService mountService = getMountService();
207 String extStoragePath = Environment.getExternalStorageDirectory().toString();
209 mSdMountToggle.setEnabled(false);
210 mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title));
211 mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary));
212 mountService.unmountVolume(extStoragePath, force);
213 } catch (RemoteException e) {
214 // Informative dialog to user that
216 showDialogInner(DLG_ERROR_UNMOUNT);
220 private void showDialogInner(int id) {
225 private boolean hasAppsAccessingStorage() throws RemoteException {
226 String extStoragePath = Environment.getExternalStorageDirectory().toString();
227 IMountService mountService = getMountService();
228 boolean showPidDialog = false;
229 int stUsers[] = mountService.getStorageUsers(extStoragePath);
230 if (stUsers != null && stUsers.length > 0) {
233 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
234 List<ApplicationInfo> list = am.getRunningExternalApplications();
235 if (list != null && list.size() > 0) {
241 private void unmount() {
242 // Check if external media is in use.
244 if (hasAppsAccessingStorage()) {
245 if (localLOGV) Log.i(TAG, "Do have storage users accessing media");
246 // Present dialog to user
247 showDialogInner(DLG_CONFIRM_UNMOUNT);
251 } catch (RemoteException e) {
252 // Very unlikely. But present an error dialog anyway
253 Log.e(TAG, "Is MountService running?");
254 showDialogInner(DLG_ERROR_UNMOUNT);
258 private void mount() {
259 IMountService mountService = getMountService();
261 if (mountService != null) {
262 mountService.mountVolume(Environment.getExternalStorageDirectory().toString());
264 Log.e(TAG, "Mount service is null, can't mount");
266 } catch (RemoteException ex) {
270 private void updateMemoryStatus() {
271 String status = Environment.getExternalStorageState();
272 String readOnly = "";
273 if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
274 status = Environment.MEDIA_MOUNTED;
275 readOnly = mRes.getString(R.string.read_only);
278 mSdFormat.setEnabled(false);
280 if (status.equals(Environment.MEDIA_MOUNTED)) {
282 File path = Environment.getExternalStorageDirectory();
283 StatFs stat = new StatFs(path.getPath());
284 long blockSize = stat.getBlockSize();
285 long totalBlocks = stat.getBlockCount();
286 long availableBlocks = stat.getAvailableBlocks();
288 mSdSize.setSummary(formatSize(totalBlocks * blockSize));
289 mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);
291 mSdMountToggle.setEnabled(true);
292 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));
293 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));
295 } catch (IllegalArgumentException e) {
296 // this can occur if the SD card is removed, but we haven't received the
297 // ACTION_MEDIA_REMOVED Intent yet.
298 status = Environment.MEDIA_REMOVED;
302 mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
303 mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
306 if (status.equals(Environment.MEDIA_UNMOUNTED) ||
307 status.equals(Environment.MEDIA_NOFS) ||
308 status.equals(Environment.MEDIA_UNMOUNTABLE) ) {
309 mSdFormat.setEnabled(true);
310 mSdMountToggle.setEnabled(true);
311 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
312 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));
314 mSdMountToggle.setEnabled(false);
315 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
316 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));
320 File path = Environment.getDataDirectory();
321 StatFs stat = new StatFs(path.getPath());
322 long blockSize = stat.getBlockSize();
323 long availableBlocks = stat.getAvailableBlocks();
324 findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks * blockSize));
327 private String formatSize(long size) {
328 return Formatter.formatFileSize(this, size);
331 public void onCancel(DialogInterface dialog) {