1/*
2 * Copyright (C) 2006 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
17package android.os.storage;
18
19import android.content.Context;
20import android.os.Environment;
21import android.os.IBinder;
22import android.os.RemoteException;
23import android.os.ServiceManager;
24import android.test.AndroidTestCase;
25import android.util.Log;
26
27import java.io.File;
28import java.io.FileOutputStream;
29
30public class AsecTests extends AndroidTestCase {
31    private static final String SECURE_CONTAINER_PREFIX = "com.android.unittests.AsecTests.";
32    private static final boolean localLOGV = true;
33    public static final String TAG="AsecTests";
34
35    private static final String FS_FAT = "fat";
36    private static final String FS_EXT4 = "ext4";
37
38    @Override
39    protected void setUp() throws Exception {
40        super.setUp();
41        if (localLOGV) Log.i(TAG, "Cleaning out old test containers");
42        cleanupContainers();
43    }
44
45    @Override
46    protected void tearDown() throws Exception {
47        super.tearDown();
48        if (localLOGV) Log.i(TAG, "Cleaning out old test containers");
49        cleanupContainers();
50    }
51
52    private void cleanupContainers() throws RemoteException {
53        IMountService ms = getMs();
54        String[] containers = ms.getSecureContainerList();
55
56        for (int i = 0; i < containers.length; i++) {
57            if (containers[i].startsWith(SECURE_CONTAINER_PREFIX)) {
58                if (localLOGV)
59                    Log.i(TAG, "Cleaning: " + containers[i]);
60                ms.destroySecureContainer(containers[i], true);
61            }
62        }
63    }
64
65    private boolean containerExists(String localId) throws RemoteException {
66        IMountService ms = getMs();
67        String[] containers = ms.getSecureContainerList();
68        String fullId = SECURE_CONTAINER_PREFIX + localId;
69
70        for (int i = 0; i < containers.length; i++) {
71            if (containers[i].equals(fullId)) {
72                return true;
73            }
74        }
75        return false;
76    }
77
78    private int createContainer(String localId, int size, String key, String filesystem,
79            boolean isExternal) throws Exception {
80        assertTrue("Media should be mounted", isMediaMounted());
81        String fullId = SECURE_CONTAINER_PREFIX + localId;
82
83        IMountService ms = getMs();
84        return ms.createSecureContainer(fullId, size, filesystem, key, android.os.Process.myUid(),
85                isExternal);
86    }
87
88    private int mountContainer(String localId, String key) throws Exception {
89        assertTrue("Media should be mounted", isMediaMounted());
90        String fullId = SECURE_CONTAINER_PREFIX + localId;
91
92        IMountService ms = getMs();
93        return ms.mountSecureContainer(fullId, key, android.os.Process.myUid());
94    }
95
96    private int renameContainer(String localId1, String localId2) throws Exception {
97        assertTrue("Media should be mounted", isMediaMounted());
98        String fullId1 = SECURE_CONTAINER_PREFIX + localId1;
99        String fullId2 = SECURE_CONTAINER_PREFIX + localId2;
100
101        IMountService ms = getMs();
102        return ms.renameSecureContainer(fullId1, fullId2);
103    }
104
105    private int unmountContainer(String localId, boolean force) throws Exception {
106        assertTrue("Media should be mounted", isMediaMounted());
107        String fullId = SECURE_CONTAINER_PREFIX + localId;
108
109        IMountService ms = getMs();
110        return ms.unmountSecureContainer(fullId, force);
111    }
112
113    private int destroyContainer(String localId, boolean force) throws Exception {
114        assertTrue("Media should be mounted", isMediaMounted());
115        String fullId = SECURE_CONTAINER_PREFIX + localId;
116
117        IMountService ms = getMs();
118        return ms.destroySecureContainer(fullId, force);
119    }
120
121    private boolean isContainerMounted(String localId) throws Exception {
122        assertTrue("Media should be mounted", isMediaMounted());
123        String fullId = SECURE_CONTAINER_PREFIX + localId;
124
125        IMountService ms = getMs();
126        return ms.isSecureContainerMounted(fullId);
127    }
128
129    private IMountService getMs() {
130        IBinder service = ServiceManager.getService("mount");
131        if (service != null) {
132            return IMountService.Stub.asInterface(service);
133        } else {
134            Log.e(TAG, "Can't get mount service");
135        }
136        return null;
137    }
138
139    private boolean isMediaMounted() throws Exception {
140        String mPath = Environment.getExternalStorageDirectory().toString();
141        String state = getMs().getVolumeState(mPath);
142        return Environment.MEDIA_MOUNTED.equals(state);
143    }
144
145
146    /*
147     * CREATE
148     */
149
150    public void test_Fat_External_Create_Success() throws Exception {
151        if (Environment.isExternalStorageEmulated()) {
152            return;
153        }
154
155        assertEquals(StorageResultCode.OperationSucceeded,
156                createContainer("testCreateContainer", 4, "none", FS_FAT, true));
157        assertTrue(containerExists("testCreateContainer"));
158    }
159
160    public void test_Ext4_External_Create_Success() throws Exception {
161        if (Environment.isExternalStorageEmulated()) {
162            return;
163        }
164
165        assertEquals(StorageResultCode.OperationSucceeded,
166                createContainer("testCreateContainer", 4, "none", FS_EXT4, true));
167        assertTrue(containerExists("testCreateContainer"));
168    }
169
170    public void test_Fat_Internal_Create_Success() throws Exception {
171        assertEquals(StorageResultCode.OperationSucceeded,
172                createContainer("testCreateContainer", 4, "none", FS_FAT, false));
173        assertTrue(containerExists("testCreateContainer"));
174    }
175
176    public void test_Ext4_Internal_Create_Success() throws Exception {
177        assertEquals(StorageResultCode.OperationSucceeded,
178                createContainer("testCreateContainer", 4, "none", FS_EXT4, false));
179        assertTrue(containerExists("testCreateContainer"));
180    }
181
182
183    /*
184     * CREATE MIN SIZE
185     */
186
187    public void test_Fat_External_CreateMinSize_Success() throws Exception {
188        if (Environment.isExternalStorageEmulated()) {
189            return;
190        }
191
192        assertEquals(StorageResultCode.OperationSucceeded,
193                createContainer("testCreateContainer", 1, "none", FS_FAT, true));
194        assertTrue(containerExists("testCreateContainer"));
195    }
196
197    public void test_Ext4_External_CreateMinSize_Success() throws Exception {
198        if (Environment.isExternalStorageEmulated()) {
199            return;
200        }
201
202        assertEquals(StorageResultCode.OperationSucceeded,
203                createContainer("testCreateContainer", 1, "none", FS_EXT4, true));
204        assertTrue(containerExists("testCreateContainer"));
205    }
206
207    public void test_Fat_Internal_CreateMinSize_Success() throws Exception {
208        assertEquals(StorageResultCode.OperationSucceeded,
209                createContainer("testCreateContainer", 1, "none", FS_FAT, false));
210        assertTrue(containerExists("testCreateContainer"));
211    }
212
213    public void test_Ext4_Internal_CreateMinSize_Success() throws Exception {
214        assertEquals(StorageResultCode.OperationSucceeded,
215                createContainer("testCreateContainer", 1, "none", FS_EXT4, false));
216        assertTrue(containerExists("testCreateContainer"));
217    }
218
219
220    /*
221     * CREATE ZERO SIZE - FAIL CASE
222     */
223
224    public void test_Fat_External_CreateZeroSize_Failure() throws Exception {
225        if (Environment.isExternalStorageEmulated()) {
226            return;
227        }
228
229        assertEquals(StorageResultCode.OperationFailedInternalError,
230                createContainer("testCreateZeroContainer", 0, "none", FS_FAT, true));
231    }
232
233    public void test_Ext4_External_CreateZeroSize_Failure() throws Exception {
234        if (Environment.isExternalStorageEmulated()) {
235            return;
236        }
237
238        assertEquals(StorageResultCode.OperationFailedInternalError,
239                createContainer("testCreateZeroContainer", 0, "none", FS_EXT4, true));
240    }
241
242    public void test_Fat_Internal_CreateZeroSize_Failure() throws Exception {
243        assertEquals(StorageResultCode.OperationFailedInternalError,
244                createContainer("testCreateZeroContainer", 0, "none", FS_FAT, false));
245    }
246
247    public void test_Ext4_Internal_CreateZeroSize_Failure() throws Exception {
248        assertEquals(StorageResultCode.OperationFailedInternalError,
249                createContainer("testCreateZeroContainer", 0, "none", FS_EXT4, false));
250    }
251
252
253    /*
254     * CREATE DUPLICATE - FAIL CASE
255     */
256
257    public void test_Fat_External_CreateDuplicate_Failure() throws Exception {
258        if (Environment.isExternalStorageEmulated()) {
259            return;
260        }
261
262        assertEquals(StorageResultCode.OperationSucceeded,
263                createContainer("testCreateDupContainer", 4, "none", FS_FAT, true));
264
265        assertEquals(StorageResultCode.OperationFailedInternalError,
266                createContainer("testCreateDupContainer", 4, "none", FS_FAT, true));
267    }
268
269    public void test_Ext4_External_CreateDuplicate_Failure() throws Exception {
270        if (Environment.isExternalStorageEmulated()) {
271            return;
272        }
273
274        assertEquals(StorageResultCode.OperationSucceeded,
275                createContainer("testCreateDupContainer", 4, "none", FS_EXT4, true));
276
277        assertEquals(StorageResultCode.OperationFailedInternalError,
278                createContainer("testCreateDupContainer", 4, "none", FS_EXT4, true));
279    }
280
281    public void test_Fat_Internal_CreateDuplicate_Failure() throws Exception {
282        assertEquals(StorageResultCode.OperationSucceeded,
283                createContainer("testCreateDupContainer", 4, "none", FS_FAT, false));
284
285        assertEquals(StorageResultCode.OperationFailedInternalError,
286                createContainer("testCreateDupContainer", 4, "none", FS_FAT, false));
287    }
288
289    public void test_Ext4_Internal_CreateDuplicate_Failure() throws Exception {
290        assertEquals(StorageResultCode.OperationSucceeded,
291                createContainer("testCreateDupContainer", 4, "none", FS_EXT4, false));
292
293        assertEquals(StorageResultCode.OperationFailedInternalError,
294                createContainer("testCreateDupContainer", 4, "none", FS_EXT4, false));
295    }
296
297
298    /*
299     * DESTROY
300     */
301
302    public void test_Fat_External_Destroy_Success() throws Exception {
303        if (Environment.isExternalStorageEmulated()) {
304            return;
305        }
306
307        assertEquals(StorageResultCode.OperationSucceeded,
308                createContainer("testDestroyContainer", 4, "none", FS_FAT, true));
309        assertEquals(StorageResultCode.OperationSucceeded,
310                destroyContainer("testDestroyContainer", false));
311    }
312
313    public void test_Ext4_External_Destroy_Success() throws Exception {
314        if (Environment.isExternalStorageEmulated()) {
315            return;
316        }
317
318        assertEquals(StorageResultCode.OperationSucceeded,
319                createContainer("testDestroyContainer", 4, "none", FS_EXT4, true));
320        assertEquals(StorageResultCode.OperationSucceeded,
321                destroyContainer("testDestroyContainer", false));
322    }
323
324    public void test_Fat_Internal_Destroy_Success() throws Exception {
325        assertEquals(StorageResultCode.OperationSucceeded,
326                createContainer("testDestroyContainer", 4, "none", FS_FAT, false));
327        assertEquals(StorageResultCode.OperationSucceeded,
328                destroyContainer("testDestroyContainer", false));
329    }
330
331    public void test_Ext4_Internal_Destroy_Success() throws Exception {
332        assertEquals(StorageResultCode.OperationSucceeded,
333                createContainer("testDestroyContainer", 4, "none", FS_EXT4, false));
334        assertEquals(StorageResultCode.OperationSucceeded,
335                destroyContainer("testDestroyContainer", false));
336    }
337
338
339    /*
340     * MOUNT
341     */
342
343    public void test_Fat_External_Mount() throws Exception {
344        if (Environment.isExternalStorageEmulated()) {
345            return;
346        }
347
348        assertEquals(StorageResultCode.OperationSucceeded,
349                createContainer("testMountContainer", 4, "none", FS_FAT, true));
350
351        assertEquals(StorageResultCode.OperationSucceeded,
352                unmountContainer("testMountContainer", false));
353
354        assertEquals(StorageResultCode.OperationSucceeded,
355                mountContainer("testMountContainer", "none"));
356    }
357
358
359    /*
360     * MOUNT BAD KEY - FAIL CASE
361     */
362
363    public void test_Fat_External_MountBadKey_Failure() throws Exception {
364        if (Environment.isExternalStorageEmulated()) {
365            return;
366        }
367
368        assertEquals(StorageResultCode.OperationSucceeded,
369                createContainer("testMountBadKey", 4, "00000000000000000000000000000000", FS_FAT,
370                        true));
371
372        assertEquals(StorageResultCode.OperationSucceeded,
373                unmountContainer("testMountBadKey", false));
374
375        assertEquals(StorageResultCode.OperationFailedInternalError,
376                mountContainer("testMountContainer", "000000000000000000000000000000001"));
377
378        assertEquals(StorageResultCode.OperationFailedInternalError,
379                mountContainer("testMountContainer", "none"));
380    }
381
382
383    public void test_Fat_External_UnmountBusy_Success() throws Exception {
384        if (Environment.isExternalStorageEmulated()) {
385            return;
386        }
387
388        IMountService ms = getMs();
389        assertEquals(StorageResultCode.OperationSucceeded,
390                createContainer("testUnmountBusyContainer", 4, "none", FS_FAT, true));
391
392        String path = ms.getSecureContainerPath(SECURE_CONTAINER_PREFIX
393                + "testUnmountBusyContainer");
394
395        File f = new File(path, "reference");
396        FileOutputStream fos = new FileOutputStream(f);
397
398        assertEquals(StorageResultCode.OperationFailedStorageBusy,
399                unmountContainer("testUnmountBusyContainer", false));
400
401        fos.close();
402        assertEquals(StorageResultCode.OperationSucceeded,
403                unmountContainer("testUnmountBusyContainer", false));
404    }
405
406    public void test_Fat_External_DestroyBusy() throws Exception {
407        if (Environment.isExternalStorageEmulated()) {
408            return;
409        }
410
411        IMountService ms = getMs();
412
413        assertEquals(StorageResultCode.OperationSucceeded,
414                createContainer("testDestroyBusyContainer", 4, "none", FS_FAT, true));
415
416        String path = ms.getSecureContainerPath(SECURE_CONTAINER_PREFIX
417                + "testDestroyBusyContainer");
418
419        File f = new File(path, "reference");
420        FileOutputStream fos = new FileOutputStream(f);
421
422        assertEquals(StorageResultCode.OperationFailedStorageBusy,
423                destroyContainer("testDestroyBusyContainer", false));
424
425        fos.close();
426        assertEquals(StorageResultCode.OperationSucceeded,
427                destroyContainer("testDestroyBusyContainer", false));
428    }
429
430    public void test_Fat_External_Rename_Success() throws Exception {
431        if (Environment.isExternalStorageEmulated()) {
432            return;
433        }
434
435        assertEquals(StorageResultCode.OperationSucceeded,
436                createContainer("testRenameContainer.1", 4, "none", FS_FAT, true));
437
438        assertEquals(StorageResultCode.OperationSucceeded,
439                unmountContainer("testRenameContainer.1", false));
440
441        assertEquals(StorageResultCode.OperationSucceeded,
442                renameContainer("testRenameContainer.1", "testRenameContainer.2"));
443
444        assertFalse(containerExists("testRenameContainer.1"));
445        assertTrue(containerExists("testRenameContainer.2"));
446    }
447
448    public void test_Fat_External_RenameSrcMounted_Failure() throws Exception {
449        if (Environment.isExternalStorageEmulated()) {
450            return;
451        }
452
453        assertEquals(StorageResultCode.OperationSucceeded,
454                createContainer("testRenameContainer.1", 4, "none", FS_FAT, true));
455
456        assertEquals(StorageResultCode.OperationFailedStorageMounted,
457                renameContainer("testRenameContainer.1", "testRenameContainer.2"));
458    }
459
460    public void test_Fat_External_RenameDstMounted_Failure() throws Exception {
461        if (Environment.isExternalStorageEmulated()) {
462            return;
463        }
464
465        assertEquals(StorageResultCode.OperationSucceeded,
466                createContainer("testRenameContainer.1", 4, "none", FS_FAT, true));
467
468        assertEquals(StorageResultCode.OperationSucceeded,
469                unmountContainer("testRenameContainer.1", false));
470
471        assertEquals(StorageResultCode.OperationSucceeded,
472                createContainer("testRenameContainer.2", 4, "none", FS_FAT, true));
473
474        assertEquals(StorageResultCode.OperationFailedStorageMounted,
475                renameContainer("testRenameContainer.1", "testRenameContainer.2"));
476    }
477
478    public void test_Fat_External_Size_Success() throws Exception {
479        if (Environment.isExternalStorageEmulated()) {
480            return;
481        }
482
483        IMountService ms = getMs();
484        assertEquals(StorageResultCode.OperationSucceeded,
485                createContainer("testContainerSize", 1, "none", FS_FAT, true));
486        String path = ms.getSecureContainerPath(SECURE_CONTAINER_PREFIX + "testContainerSize");
487
488        byte[] buf = new byte[4096];
489        File f = new File(path, "reference");
490        FileOutputStream fos = new FileOutputStream(f);
491        for (int i = 0; i < (1024 * 1024); i += buf.length) {
492            fos.write(buf);
493        }
494        fos.close();
495    }
496
497    public void testGetSecureContainerPath_NonExistPath_Failure() throws Exception {
498        IMountService ms = getMs();
499        assertNull("Getting the path for an invalid container should return null",
500                ms.getSecureContainerPath("jparks.broke.it"));
501    }
502
503    /*------------ Tests for unmounting volume ---*/
504    public final long MAX_WAIT_TIME=120*1000;
505    public final long WAIT_TIME_INCR=20*1000;
506
507    boolean getMediaState() throws Exception {
508        String mPath = Environment.getExternalStorageDirectory().toString();
509        String state = getMs().getVolumeState(mPath);
510        return Environment.MEDIA_MOUNTED.equals(state);
511    }
512
513    boolean mountMedia() throws Exception {
514        if (Environment.isExternalStorageEmulated()) {
515            return true;
516        }
517
518        if (getMediaState()) {
519            return true;
520        }
521
522        String mPath = Environment.getExternalStorageDirectory().toString();
523        int ret = getMs().mountVolume(mPath);
524        return ret == StorageResultCode.OperationSucceeded;
525    }
526
527    class StorageListener extends StorageEventListener {
528        String oldState;
529        String newState;
530        String path;
531        private boolean doneFlag = false;
532
533        public void action() {
534            synchronized (this) {
535                doneFlag = true;
536                notifyAll();
537            }
538        }
539
540        public boolean isDone() {
541            return doneFlag;
542        }
543
544        @Override
545        public void onStorageStateChanged(String path, String oldState, String newState) {
546            if (localLOGV) Log.i(TAG, "Storage state changed from " + oldState + " to " + newState);
547            this.oldState = oldState;
548            this.newState = newState;
549            this.path = path;
550            action();
551        }
552    }
553
554    private void unmountMedia() throws Exception {
555        if (Environment.isExternalStorageEmulated()) {
556            return;
557        }
558
559        if (!getMediaState()) {
560            return;
561        }
562
563        String path = Environment.getExternalStorageDirectory().toString();
564        StorageListener observer = new StorageListener();
565        StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
566        sm.registerListener(observer);
567        try {
568            // Wait on observer
569            synchronized(observer) {
570                getMs().unmountVolume(path, false, false);
571                long waitTime = 0;
572                while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
573                    observer.wait(WAIT_TIME_INCR);
574                    waitTime += WAIT_TIME_INCR;
575                }
576                if(!observer.isDone()) {
577                    fail("Timed out waiting for packageInstalled callback");
578                }
579            }
580        } finally {
581            sm.unregisterListener(observer);
582        }
583    }
584
585    public void testUnmount() throws Exception {
586        boolean oldStatus = getMediaState();
587        Log.i(TAG, "oldStatus="+oldStatus);
588        try {
589            // Mount media firsts
590            if (!getMediaState()) {
591                mountMedia();
592            }
593            unmountMedia();
594        } finally {
595            // Restore old status
596            boolean currStatus = getMediaState();
597            if (oldStatus != currStatus) {
598                if (oldStatus) {
599                    // Mount media
600                    mountMedia();
601                } else {
602                    unmountMedia();
603                }
604            }
605        }
606    }
607
608    class MultipleStorageLis extends StorageListener {
609        int count = 0;
610        public void onStorageStateChanged(String path, String oldState, String newState) {
611            count++;
612            super.action();
613        }
614    }
615    /*
616     * This test invokes unmount multiple time and expects the call back
617     * to be invoked just once.
618     */
619    public void testUnmountMultiple() throws Exception {
620        if (Environment.isExternalStorageEmulated()) {
621            return;
622        }
623
624        boolean oldStatus = getMediaState();
625        StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
626        MultipleStorageLis observer = new MultipleStorageLis();
627        try {
628            // Mount media firsts
629            if (!getMediaState()) {
630                mountMedia();
631            }
632            String path = Environment.getExternalStorageDirectory().toString();
633            sm.registerListener(observer);
634            // Wait on observer
635            synchronized(observer) {
636                for (int i = 0; i < 5; i++) {
637                    getMs().unmountVolume(path, false, false);
638                }
639                long waitTime = 0;
640                while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
641                    observer.wait(WAIT_TIME_INCR);
642                    waitTime += WAIT_TIME_INCR;
643                }
644                if(!observer.isDone()) {
645                    fail("Timed out waiting for packageInstalled callback");
646                }
647            }
648            assertEquals(observer.count, 1);
649        } finally {
650            sm.unregisterListener(observer);
651            // Restore old status
652            boolean currStatus = getMediaState();
653            if (oldStatus != currStatus) {
654                if (oldStatus) {
655                    // Mount media
656                    mountMedia();
657                } else {
658                    unmountMedia();
659                }
660            }
661        }
662    }
663
664    class ShutdownObserver extends  IMountShutdownObserver.Stub{
665        private boolean doneFlag = false;
666        int statusCode;
667
668        public void action() {
669            synchronized (this) {
670                doneFlag = true;
671                notifyAll();
672            }
673        }
674
675        public boolean isDone() {
676            return doneFlag;
677        }
678        public void onShutDownComplete(int statusCode) throws RemoteException {
679            this.statusCode = statusCode;
680            action();
681        }
682
683    }
684
685    void invokeShutdown() throws Exception {
686        IMountService ms = getMs();
687        ShutdownObserver observer = new ShutdownObserver();
688        synchronized (observer) {
689            ms.shutdown(observer);
690        }
691    }
692
693    public void testShutdown() throws Exception {
694        if (Environment.isExternalStorageEmulated()) {
695            return;
696        }
697
698        boolean oldStatus = getMediaState();
699        try {
700            // Mount media firsts
701            if (!getMediaState()) {
702                mountMedia();
703            }
704            invokeShutdown();
705        } finally {
706            // Restore old status
707            boolean currStatus = getMediaState();
708            if (oldStatus != currStatus) {
709                if (oldStatus) {
710                    // Mount media
711                    mountMedia();
712                } else {
713                    unmountMedia();
714                }
715            }
716        }
717    }
718
719    /*
720     * This test invokes unmount multiple time and expects the call back
721     * to be invoked just once.
722     */
723    public void testShutdownMultiple() throws Exception {
724        if (Environment.isExternalStorageEmulated()) {
725            return;
726        }
727
728        boolean oldStatus = getMediaState();
729        try {
730            // Mount media firsts
731            if (!getMediaState()) {
732                mountMedia();
733            }
734            IMountService ms = getMs();
735            ShutdownObserver observer = new ShutdownObserver();
736            synchronized (observer) {
737                ms.shutdown(observer);
738                for (int i = 0; i < 4; i++) {
739                    ms.shutdown(null);
740                }
741            }
742        } finally {
743            // Restore old status
744            boolean currStatus = getMediaState();
745            if (oldStatus != currStatus) {
746                if (oldStatus) {
747                    // Mount media
748                    mountMedia();
749                } else {
750                    unmountMedia();
751                }
752            }
753        }
754    }
755
756}
757