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.AlertDialog;
20 import android.app.Dialog;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.DialogInterface.OnCancelListener;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.res.Resources;
28 import android.os.Bundle;
29 import android.os.Environment;
30 import android.os.IBinder;
31 import android.os.Parcelable;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.storage.IMountService;
35 import android.os.storage.StorageEventListener;
36 import android.os.storage.StorageManager;
37 import android.os.storage.StorageVolume;
38 import android.preference.Preference;
39 import android.preference.PreferenceScreen;
40 import android.util.Log;
41 import android.widget.Toast;
43 import com.android.settings.R;
44 import com.android.settings.SettingsPreferenceFragment;
46 public class Memory extends SettingsPreferenceFragment implements OnCancelListener {
47 private static final String TAG = "MemorySettings";
49 private static final int DLG_CONFIRM_UNMOUNT = 1;
50 private static final int DLG_ERROR_UNMOUNT = 2;
52 private Resources mResources;
54 // The mountToggle Preference that has been clicked.
55 // The click event will be discarded if this value is not null. Reset to null after (un)mount.
56 private Preference mClickedMountToggle;
57 private String mClickedMountPoint;
59 // Access using getMountService()
60 private IMountService mMountService = null;
62 private StorageManager mStorageManager = null;
64 private StorageVolumePreferenceCategory[] mStorageVolumePreferenceCategories;
67 public void onCreate(Bundle icicle) {
68 super.onCreate(icicle);
70 if (mStorageManager == null) {
71 mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
72 mStorageManager.registerListener(mStorageListener);
75 addPreferencesFromResource(R.xml.device_info_memory);
77 mResources = getResources();
80 IMountService mountService = IMountService.Stub.asInterface(ServiceManager
81 .getService("mount"));
82 Parcelable[] volumes = mountService.getVolumeList();
83 int length = volumes.length;
84 mStorageVolumePreferenceCategories = new StorageVolumePreferenceCategory[length];
85 for (int i = 0; i < length; i++) {
86 StorageVolume storageVolume = (StorageVolume) volumes[i];
87 StorageVolumePreferenceCategory storagePreferenceCategory =
88 new StorageVolumePreferenceCategory(getActivity(), mResources, storageVolume,
89 i == 0); // The first volume is the primary volume
90 mStorageVolumePreferenceCategories[i] = storagePreferenceCategory;
91 getPreferenceScreen().addPreference(storagePreferenceCategory);
92 storagePreferenceCategory.init();
94 } catch (Exception e) {
95 Log.e(TAG, "couldn't talk to MountService", e);
100 public void onResume() {
102 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
103 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
104 intentFilter.addDataScheme("file");
105 getActivity().registerReceiver(mMediaScannerReceiver, intentFilter);
107 for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
108 mStorageVolumePreferenceCategories[i].onResume();
112 StorageEventListener mStorageListener = new StorageEventListener() {
114 public void onStorageStateChanged(String path, String oldState, String newState) {
115 Log.i(TAG, "Received storage state changed notification that " +
116 path + " changed state from " + oldState +
118 for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
119 StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i];
120 if (path.equals(svpc.getMountPoint())) {
121 svpc.onStorageStateChanged();
129 public void onPause() {
131 getActivity().unregisterReceiver(mMediaScannerReceiver);
132 for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
133 mStorageVolumePreferenceCategories[i].onPause();
138 public void onDestroy() {
139 if (mStorageManager != null && mStorageListener != null) {
140 mStorageManager.unregisterListener(mStorageListener);
145 private synchronized IMountService getMountService() {
146 if (mMountService == null) {
147 IBinder service = ServiceManager.getService("mount");
148 if (service != null) {
149 mMountService = IMountService.Stub.asInterface(service);
151 Log.e(TAG, "Can't get mount service");
154 return mMountService;
158 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
159 for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
160 StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i];
161 Intent intent = svpc.intentForClick(preference);
162 if (intent != null) {
163 startActivity(intent);
167 boolean mountToggleClicked = svpc.mountToggleClicked(preference);
168 if (mountToggleClicked && mClickedMountToggle == null) {
169 mClickedMountToggle = preference;
170 mClickedMountPoint = svpc.getMountPoint();
171 String state = svpc.getStorageVolumeState();
172 if (state.equals(Environment.MEDIA_MOUNTED) ||
173 state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
185 private final BroadcastReceiver mMediaScannerReceiver = new BroadcastReceiver() {
187 public void onReceive(Context context, Intent intent) {
188 for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
189 mStorageVolumePreferenceCategories[i].onMediaScannerFinished();
195 public Dialog onCreateDialog(int id) {
197 case DLG_CONFIRM_UNMOUNT:
198 return new AlertDialog.Builder(getActivity())
199 .setTitle(R.string.dlg_confirm_unmount_title)
200 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
201 public void onClick(DialogInterface dialog, int which) {
204 .setNegativeButton(R.string.cancel, null)
205 .setMessage(R.string.dlg_confirm_unmount_text)
207 case DLG_ERROR_UNMOUNT:
208 return new AlertDialog.Builder(getActivity())
209 .setTitle(R.string.dlg_error_unmount_title)
210 .setNeutralButton(R.string.dlg_ok, null)
211 .setMessage(R.string.dlg_error_unmount_text)
218 protected void showDialog(int id) {
219 super.showDialog(id);
222 case DLG_CONFIRM_UNMOUNT:
223 case DLG_ERROR_UNMOUNT:
224 setOnCancelListener(this);
229 private void doUnmount() {
230 // Present a toast here
231 Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
232 IMountService mountService = getMountService();
234 mClickedMountToggle.setEnabled(false);
235 mClickedMountToggle.setTitle(mResources.getString(R.string.sd_ejecting_title));
236 mClickedMountToggle.setSummary(mResources.getString(R.string.sd_ejecting_summary));
237 mountService.unmountVolume(mClickedMountPoint, true);
238 } catch (RemoteException e) {
239 // Informative dialog to user that unmount failed.
240 showDialogInner(DLG_ERROR_UNMOUNT);
242 mClickedMountToggle = null;
245 private void showDialogInner(int id) {
250 private boolean hasAppsAccessingStorage() throws RemoteException {
251 IMountService mountService = getMountService();
252 int stUsers[] = mountService.getStorageUsers(mClickedMountPoint);
253 if (stUsers != null && stUsers.length > 0) {
256 // TODO FIXME Parameterize with mountPoint and uncomment.
257 // On HC-MR2, no apps can be installed on sd and the emulated internal storage is not
258 // removable: application cannot interfere with unmount
260 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
261 List<ApplicationInfo> list = am.getRunningExternalApplications();
262 if (list != null && list.size() > 0) {
269 private void unmount() {
270 // Check if external media is in use.
272 if (hasAppsAccessingStorage()) {
273 // Present dialog to user
274 showDialogInner(DLG_CONFIRM_UNMOUNT);
278 } catch (RemoteException e) {
279 // Very unlikely. But present an error dialog anyway
280 Log.e(TAG, "Is MountService running?");
281 showDialogInner(DLG_ERROR_UNMOUNT);
282 mClickedMountToggle = null;
286 private void mount() {
287 IMountService mountService = getMountService();
289 if (mountService != null) {
290 mountService.mountVolume(mClickedMountPoint);
292 Log.e(TAG, "Mount service is null, can't mount");
294 } catch (RemoteException ex) {
295 // Not much can be done
297 mClickedMountToggle = null;
300 public void onCancel(DialogInterface dialog) {
301 mClickedMountToggle = null;