• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.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.Intent;
25 import android.content.IntentFilter;
26 import android.content.res.Resources;
27 import android.os.Bundle;
28 import android.os.Environment;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.os.ServiceManager;
32 import android.os.storage.IMountService;
33 import android.os.storage.StorageEventListener;
34 import android.os.storage.StorageManager;
35 import android.os.storage.StorageVolume;
36 import android.preference.Preference;
37 import android.preference.PreferenceActivity;
38 import android.preference.PreferenceScreen;
39 import android.util.Log;
40 import android.view.Menu;
41 import android.view.MenuInflater;
42 import android.view.MenuItem;
43 import android.widget.Toast;
44 
45 import com.android.settings.R;
46 import com.android.settings.SettingsPreferenceFragment;
47 import com.android.settings.Utils;
48 
49 public class Memory extends SettingsPreferenceFragment {
50     private static final String TAG = "MemorySettings";
51 
52     private static final int DLG_CONFIRM_UNMOUNT = 1;
53     private static final int DLG_ERROR_UNMOUNT = 2;
54 
55     private Resources mResources;
56 
57     // The mountToggle Preference that has last been clicked.
58     // Assumes no two successive unmount event on 2 different volumes are performed before the first
59     // one's preference is disabled
60     private Preference mLastClickedMountToggle;
61     private String mClickedMountPoint;
62 
63     // Access using getMountService()
64     private IMountService mMountService = null;
65 
66     private StorageManager mStorageManager = null;
67 
68     private StorageVolumePreferenceCategory mInternalStorageVolumePreferenceCategory;
69     private StorageVolumePreferenceCategory[] mStorageVolumePreferenceCategories;
70 
71     @Override
onCreate(Bundle icicle)72     public void onCreate(Bundle icicle) {
73         super.onCreate(icicle);
74 
75         if (mStorageManager == null) {
76             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
77             mStorageManager.registerListener(mStorageListener);
78         }
79 
80         addPreferencesFromResource(R.xml.device_info_memory);
81 
82         mResources = getResources();
83 
84         if (!Environment.isExternalStorageEmulated()) {
85             // External storage is separate from internal storage; need to
86             // show internal storage as a separate item.
87             mInternalStorageVolumePreferenceCategory = new StorageVolumePreferenceCategory(
88                     getActivity(), mResources, null, mStorageManager, false);
89             getPreferenceScreen().addPreference(mInternalStorageVolumePreferenceCategory);
90             mInternalStorageVolumePreferenceCategory.init();
91         }
92 
93         StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
94         int length = storageVolumes.length;
95         mStorageVolumePreferenceCategories = new StorageVolumePreferenceCategory[length];
96         for (int i = 0; i < length; i++) {
97             StorageVolume storageVolume = storageVolumes[i];
98             boolean isPrimary = i == 0;
99             mStorageVolumePreferenceCategories[i] = new StorageVolumePreferenceCategory(
100                     getActivity(), mResources, storageVolume, mStorageManager, isPrimary);
101             getPreferenceScreen().addPreference(mStorageVolumePreferenceCategories[i]);
102             mStorageVolumePreferenceCategories[i].init();
103         }
104 
105         setHasOptionsMenu(true);
106     }
107 
isMassStorageEnabled()108     private boolean isMassStorageEnabled() {
109         // mass storage is enabled if primary volume supports it
110         final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
111         return (storageVolumes.length > 0 && storageVolumes[0].allowMassStorage());
112     }
113 
114     @Override
onResume()115     public void onResume() {
116         super.onResume();
117         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
118         intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
119         intentFilter.addDataScheme("file");
120         getActivity().registerReceiver(mMediaScannerReceiver, intentFilter);
121 
122         if (mInternalStorageVolumePreferenceCategory != null) {
123             mInternalStorageVolumePreferenceCategory.onResume();
124         }
125         for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
126             mStorageVolumePreferenceCategories[i].onResume();
127         }
128     }
129 
130     StorageEventListener mStorageListener = new StorageEventListener() {
131         @Override
132         public void onStorageStateChanged(String path, String oldState, String newState) {
133             Log.i(TAG, "Received storage state changed notification that " + path +
134                     " changed state from " + oldState + " to " + newState);
135             for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
136                 StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i];
137                 if (path.equals(svpc.getStorageVolume().getPath())) {
138                     svpc.onStorageStateChanged();
139                     break;
140                 }
141             }
142         }
143     };
144 
145     @Override
onPause()146     public void onPause() {
147         super.onPause();
148         getActivity().unregisterReceiver(mMediaScannerReceiver);
149         if (mInternalStorageVolumePreferenceCategory != null) {
150             mInternalStorageVolumePreferenceCategory.onPause();
151         }
152         for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
153             mStorageVolumePreferenceCategories[i].onPause();
154         }
155     }
156 
157     @Override
onDestroy()158     public void onDestroy() {
159         if (mStorageManager != null && mStorageListener != null) {
160             mStorageManager.unregisterListener(mStorageListener);
161         }
162         super.onDestroy();
163     }
164 
165     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)166     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
167         inflater.inflate(R.menu.storage, menu);
168     }
169 
170     @Override
onPrepareOptionsMenu(Menu menu)171     public void onPrepareOptionsMenu(Menu menu) {
172         final MenuItem usb = menu.findItem(R.id.storage_usb);
173         usb.setVisible(!isMassStorageEnabled());
174     }
175 
176     @Override
onOptionsItemSelected(MenuItem item)177     public boolean onOptionsItemSelected(MenuItem item) {
178         switch (item.getItemId()) {
179             case R.id.storage_usb:
180                 if (getActivity() instanceof PreferenceActivity) {
181                     ((PreferenceActivity) getActivity()).startPreferencePanel(
182                             UsbSettings.class.getCanonicalName(),
183                             null,
184                             R.string.storage_title_usb, null,
185                             this, 0);
186                 } else {
187                     startFragment(this, UsbSettings.class.getCanonicalName(), -1, null);
188                 }
189                 return true;
190         }
191         return super.onOptionsItemSelected(item);
192     }
193 
getMountService()194     private synchronized IMountService getMountService() {
195        if (mMountService == null) {
196            IBinder service = ServiceManager.getService("mount");
197            if (service != null) {
198                mMountService = IMountService.Stub.asInterface(service);
199            } else {
200                Log.e(TAG, "Can't get mount service");
201            }
202        }
203        return mMountService;
204     }
205 
206     @Override
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)207     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
208         for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
209             StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i];
210             Intent intent = svpc.intentForClick(preference);
211             if (intent != null) {
212                 // Don't go across app boundary if monkey is running
213                 if (!Utils.isMonkeyRunning()) {
214                     startActivity(intent);
215                 }
216                 return true;
217             }
218 
219             if (svpc.mountToggleClicked(preference)) {
220                 mLastClickedMountToggle = preference;
221                 final StorageVolume storageVolume = svpc.getStorageVolume();
222                 mClickedMountPoint = storageVolume.getPath();
223                 String state = mStorageManager.getVolumeState(storageVolume.getPath());
224                 if (Environment.MEDIA_MOUNTED.equals(state) ||
225                         Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
226                     unmount();
227                 } else {
228                     mount();
229                 }
230                 return true;
231             }
232         }
233 
234         return false;
235     }
236 
237     private final BroadcastReceiver mMediaScannerReceiver = new BroadcastReceiver() {
238         @Override
239         public void onReceive(Context context, Intent intent) {
240             // mInternalStorageVolumePreferenceCategory is not affected by the media scanner
241             for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
242                 mStorageVolumePreferenceCategories[i].onMediaScannerFinished();
243             }
244         }
245     };
246 
247     @Override
onCreateDialog(int id)248     public Dialog onCreateDialog(int id) {
249         switch (id) {
250         case DLG_CONFIRM_UNMOUNT:
251                 return new AlertDialog.Builder(getActivity())
252                     .setTitle(R.string.dlg_confirm_unmount_title)
253                     .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
254                         public void onClick(DialogInterface dialog, int which) {
255                             doUnmount();
256                         }})
257                     .setNegativeButton(R.string.cancel, null)
258                     .setMessage(R.string.dlg_confirm_unmount_text)
259                     .create();
260         case DLG_ERROR_UNMOUNT:
261                 return new AlertDialog.Builder(getActivity())
262             .setTitle(R.string.dlg_error_unmount_title)
263             .setNeutralButton(R.string.dlg_ok, null)
264             .setMessage(R.string.dlg_error_unmount_text)
265             .create();
266         }
267         return null;
268     }
269 
270     private void doUnmount() {
271         // Present a toast here
272         Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
273         IMountService mountService = getMountService();
274         try {
275             mLastClickedMountToggle.setEnabled(false);
276             mLastClickedMountToggle.setTitle(mResources.getString(R.string.sd_ejecting_title));
277             mLastClickedMountToggle.setSummary(mResources.getString(R.string.sd_ejecting_summary));
278             mountService.unmountVolume(mClickedMountPoint, true, false);
279         } catch (RemoteException e) {
280             // Informative dialog to user that unmount failed.
281             showDialogInner(DLG_ERROR_UNMOUNT);
282         }
283     }
284 
285     private void showDialogInner(int id) {
286         removeDialog(id);
287         showDialog(id);
288     }
289 
290     private boolean hasAppsAccessingStorage() throws RemoteException {
291         IMountService mountService = getMountService();
292         int stUsers[] = mountService.getStorageUsers(mClickedMountPoint);
293         if (stUsers != null && stUsers.length > 0) {
294             return true;
295         }
296         // TODO FIXME Parameterize with mountPoint and uncomment.
297         // On HC-MR2, no apps can be installed on sd and the emulated internal storage is not
298         // removable: application cannot interfere with unmount
299         /*
300         ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
301         List<ApplicationInfo> list = am.getRunningExternalApplications();
302         if (list != null && list.size() > 0) {
303             return true;
304         }
305         */
306         // Better safe than sorry. Assume the storage is used to ask for confirmation.
307         return true;
308     }
309 
310     private void unmount() {
311         // Check if external media is in use.
312         try {
313            if (hasAppsAccessingStorage()) {
314                // Present dialog to user
315                showDialogInner(DLG_CONFIRM_UNMOUNT);
316            } else {
317                doUnmount();
318            }
319         } catch (RemoteException e) {
320             // Very unlikely. But present an error dialog anyway
321             Log.e(TAG, "Is MountService running?");
322             showDialogInner(DLG_ERROR_UNMOUNT);
323         }
324     }
325 
326     private void mount() {
327         IMountService mountService = getMountService();
328         try {
329             if (mountService != null) {
330                 mountService.mountVolume(mClickedMountPoint);
331             } else {
332                 Log.e(TAG, "Mount service is null, can't mount");
333             }
334         } catch (RemoteException ex) {
335             // Not much can be done
336         }
337     }
338 }
339