StorageManager.java revision bf0cb26a1c6305f2a7795c2498591b6189cc5b79
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
17package android.os.storage;
18
19import android.os.Handler;
20import android.os.Looper;
21import android.os.Message;
22import android.os.RemoteException;
23import android.os.ServiceManager;
24import android.util.Log;
25
26import java.util.ArrayList;
27
28/**
29 * StorageManager is the interface to the systems storage service.
30 * Get an instance of this class by calling
31 * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
32 * of {@link android.content.Context#STORAGE_SERVICE}.
33 *
34 */
35
36public class StorageManager
37{
38    private static final String TAG = "StorageManager";
39
40    /*
41     * Our internal MountService binder reference
42     */
43    private IMountService mMountService;
44
45    /*
46     * The looper target for callbacks
47     */
48    Looper mTgtLooper;
49
50    /*
51     * Target listener for binder callbacks
52     */
53    private MountServiceBinderListener mBinderListener;
54
55    /*
56     * List of our listeners
57     */
58    private ArrayList<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
59
60    private class MountServiceBinderListener extends IMountServiceListener.Stub {
61        public void onUsbMassStorageConnectionChanged(boolean available) {
62            final int size = mListeners.size();
63            for (int i = 0; i < size; i++) {
64                mListeners.get(i).sendShareAvailabilityChanged(available);
65            }
66        }
67
68        public void onStorageStateChanged(String path, String oldState, String newState) {
69            final int size = mListeners.size();
70            for (int i = 0; i < size; i++) {
71                mListeners.get(i).sendStorageStateChanged(path, oldState, newState);
72            }
73        }
74    }
75
76    /**
77     * Binder listener for OBB action results.
78     */
79    private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener();
80    private class ObbActionBinderListener extends IObbActionListener.Stub {
81        @Override
82        public void onObbResult(String filename, String status) throws RemoteException {
83            Log.i(TAG, "filename = " + filename + ", result = " + status);
84        }
85    }
86
87    /**
88     * Private base class for messages sent between the callback thread
89     * and the target looper handler.
90     */
91    private class StorageEvent {
92        public static final int EVENT_UMS_CONNECTION_CHANGED = 1;
93        public static final int EVENT_STORAGE_STATE_CHANGED   = 2;
94
95        private Message mMessage;
96
97        public StorageEvent(int what) {
98            mMessage = Message.obtain();
99            mMessage.what = what;
100            mMessage.obj = this;
101        }
102
103        public Message getMessage() {
104            return mMessage;
105        }
106    }
107
108    /**
109     * Message sent on a USB mass storage connection change.
110     */
111    private class UmsConnectionChangedStorageEvent extends StorageEvent {
112        public boolean available;
113
114        public UmsConnectionChangedStorageEvent(boolean a) {
115            super(EVENT_UMS_CONNECTION_CHANGED);
116            available = a;
117        }
118    }
119
120    /**
121     * Message sent on volume state change.
122     */
123    private class StorageStateChangedStorageEvent extends StorageEvent {
124        public String path;
125        public String oldState;
126        public String newState;
127
128        public StorageStateChangedStorageEvent(String p, String oldS, String newS) {
129            super(EVENT_STORAGE_STATE_CHANGED);
130            path = p;
131            oldState = oldS;
132            newState = newS;
133        }
134    }
135
136    /**
137     * Private class containing sender and receiver code for StorageEvents.
138     */
139    private class ListenerDelegate {
140        final StorageEventListener mStorageEventListener;
141        private final Handler mHandler;
142
143        ListenerDelegate(StorageEventListener listener) {
144            mStorageEventListener = listener;
145            mHandler = new Handler(mTgtLooper) {
146                @Override
147                public void handleMessage(Message msg) {
148                    StorageEvent e = (StorageEvent) msg.obj;
149
150                    if (msg.what == StorageEvent.EVENT_UMS_CONNECTION_CHANGED) {
151                        UmsConnectionChangedStorageEvent ev = (UmsConnectionChangedStorageEvent) e;
152                        mStorageEventListener.onUsbMassStorageConnectionChanged(ev.available);
153                    } else if (msg.what == StorageEvent.EVENT_STORAGE_STATE_CHANGED) {
154                        StorageStateChangedStorageEvent ev = (StorageStateChangedStorageEvent) e;
155                        mStorageEventListener.onStorageStateChanged(ev.path, ev.oldState, ev.newState);
156                    } else {
157                        Log.e(TAG, "Unsupported event " + msg.what);
158                    }
159                }
160            };
161        }
162
163        StorageEventListener getListener() {
164            return mStorageEventListener;
165        }
166
167        void sendShareAvailabilityChanged(boolean available) {
168            UmsConnectionChangedStorageEvent e = new UmsConnectionChangedStorageEvent(available);
169            mHandler.sendMessage(e.getMessage());
170        }
171
172        void sendStorageStateChanged(String path, String oldState, String newState) {
173            StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);
174            mHandler.sendMessage(e.getMessage());
175        }
176    }
177
178    /**
179     * Constructs a StorageManager object through which an application can
180     * can communicate with the systems mount service.
181     *
182     * @param tgtLooper The {@android.os.Looper} which events will be received on.
183     *
184     * <p>Applications can get instance of this class by calling
185     * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
186     * of {@link android.content.Context#STORAGE_SERVICE}.
187     *
188     * @hide
189     */
190    public StorageManager(Looper tgtLooper) throws RemoteException {
191        mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
192        if (mMountService == null) {
193            Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
194            return;
195        }
196        mTgtLooper = tgtLooper;
197        mBinderListener = new MountServiceBinderListener();
198        mMountService.registerListener(mBinderListener);
199    }
200
201
202    /**
203     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
204     *
205     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
206     *
207     */
208    public void registerListener(StorageEventListener listener) {
209        if (listener == null) {
210            return;
211        }
212
213        synchronized (mListeners) {
214            mListeners.add(new ListenerDelegate(listener));
215        }
216    }
217
218    /**
219     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
220     *
221     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
222     *
223     */
224    public void unregisterListener(StorageEventListener listener) {
225        if (listener == null) {
226            return;
227        }
228
229        synchronized (mListeners) {
230            final int size = mListeners.size();
231            for (int i=0 ; i<size ; i++) {
232                ListenerDelegate l = mListeners.get(i);
233                if (l.getListener() == listener) {
234                    mListeners.remove(i);
235                    break;
236                }
237            }
238        }
239    }
240
241    /**
242     * Enables USB Mass Storage (UMS) on the device.
243     */
244    public void enableUsbMassStorage() {
245        try {
246            mMountService.setUsbMassStorageEnabled(true);
247        } catch (Exception ex) {
248            Log.e(TAG, "Failed to enable UMS", ex);
249        }
250    }
251
252    /**
253     * Disables USB Mass Storage (UMS) on the device.
254     */
255    public void disableUsbMassStorage() {
256        try {
257            mMountService.setUsbMassStorageEnabled(false);
258        } catch (Exception ex) {
259            Log.e(TAG, "Failed to disable UMS", ex);
260        }
261    }
262
263    /**
264     * Query if a USB Mass Storage (UMS) host is connected.
265     * @return true if UMS host is connected.
266     */
267    public boolean isUsbMassStorageConnected() {
268        try {
269            return mMountService.isUsbMassStorageConnected();
270        } catch (Exception ex) {
271            Log.e(TAG, "Failed to get UMS connection state", ex);
272        }
273        return false;
274    }
275
276    /**
277     * Query if a USB Mass Storage (UMS) is enabled on the device.
278     * @return true if UMS host is enabled.
279     */
280    public boolean isUsbMassStorageEnabled() {
281        try {
282            return mMountService.isUsbMassStorageEnabled();
283        } catch (RemoteException rex) {
284            Log.e(TAG, "Failed to get UMS enable state", rex);
285        }
286        return false;
287    }
288
289    /**
290     * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
291     * specified, it is supplied to the mounting process to be used in any
292     * encryption used in the OBB.
293     * <p>
294     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
295     * file matches a package ID that is owned by the calling program's UID.
296     * That is, shared UID applications can obtain access to any other
297     * application's OBB that shares its UID.
298     *
299     * @param filename the path to the OBB file
300     * @param key decryption key
301     * @return whether the mount call was successfully queued or not
302     */
303    public boolean mountObb(String filename, String key) {
304        try {
305            mMountService.mountObb(filename, key, mObbActionListener);
306            return true;
307        } catch (RemoteException e) {
308            Log.e(TAG, "Failed to mount OBB", e);
309        }
310
311        return false;
312    }
313
314    /**
315     * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag
316     * is true, it will kill any application needed to unmount the given OBB.
317     * <p>
318     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
319     * file matches a package ID that is owned by the calling program's UID.
320     * That is, shared UID applications can obtain access to any other
321     * application's OBB that shares its UID.
322     *
323     * @param filename path to the OBB file
324     * @param force whether to kill any programs using this in order to unmount
325     *            it
326     * @return whether the unmount call was successfully queued or not
327     * @throws IllegalArgumentException when OBB is not already mounted
328     */
329    public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException {
330        try {
331            mMountService.unmountObb(filename, force, mObbActionListener);
332            return true;
333        } catch (RemoteException e) {
334            Log.e(TAG, "Failed to mount OBB", e);
335        }
336
337        return false;
338    }
339
340    /**
341     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
342     *
343     * @param filename path to OBB image
344     * @return true if OBB is mounted; false if not mounted or on error
345     */
346    public boolean isObbMounted(String filename) throws IllegalArgumentException {
347        try {
348            return mMountService.isObbMounted(filename);
349        } catch (RemoteException e) {
350            Log.e(TAG, "Failed to check if OBB is mounted", e);
351        }
352
353        return false;
354    }
355
356    /**
357     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
358     * give you the path to where you can obtain access to the internals of the
359     * OBB.
360     *
361     * @param filename path to OBB image
362     * @return absolute path to mounted OBB image data or <code>null</code> if
363     *         not mounted or exception encountered trying to read status
364     */
365    public String getMountedObbPath(String filename) {
366        try {
367            return mMountService.getMountedObbPath(filename);
368        } catch (RemoteException e) {
369            Log.e(TAG, "Failed to find mounted path for OBB", e);
370        } catch (IllegalArgumentException e) {
371            Log.d(TAG, "Couldn't read OBB file", e);
372        }
373
374        return null;
375    }
376}
377