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