StorageManager.java revision 74acbbb2cd367c6e78db7de5118ff9dd56da61d0
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 static android.net.TrafficStats.MB_IN_BYTES;
20
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.os.Environment;
26import android.os.FileUtils;
27import android.os.Handler;
28import android.os.Looper;
29import android.os.Message;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.provider.Settings;
33import android.text.TextUtils;
34import android.util.Log;
35import android.util.SparseArray;
36
37import com.android.internal.os.SomeArgs;
38import com.android.internal.util.Preconditions;
39
40import java.io.File;
41import java.io.IOException;
42import java.lang.ref.WeakReference;
43import java.util.ArrayList;
44import java.util.Arrays;
45import java.util.Iterator;
46import java.util.List;
47import java.util.Objects;
48import java.util.concurrent.atomic.AtomicInteger;
49
50/**
51 * StorageManager is the interface to the systems storage service. The storage
52 * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
53 * <p>
54 * OBBs contain a filesystem that maybe be encrypted on disk and mounted
55 * on-demand from an application. OBBs are a good way of providing large amounts
56 * of binary assets without packaging them into APKs as they may be multiple
57 * gigabytes in size. However, due to their size, they're most likely stored in
58 * a shared storage pool accessible from all programs. The system does not
59 * guarantee the security of the OBB file itself: if any program modifies the
60 * OBB, there is no guarantee that a read from that OBB will produce the
61 * expected output.
62 * <p>
63 * Get an instance of this class by calling
64 * {@link android.content.Context#getSystemService(java.lang.String)} with an
65 * argument of {@link android.content.Context#STORAGE_SERVICE}.
66 */
67public class StorageManager {
68    private static final String TAG = "StorageManager";
69
70    /** {@hide} */
71    public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
72    /** {@hide} */
73    public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
74
75    /** {@hide} */
76    public static final int FLAG_ALL_METADATA = 1 << 0;
77
78    private final Context mContext;
79    private final ContentResolver mResolver;
80
81    private final IMountService mMountService;
82    private final Looper mLooper;
83    private final AtomicInteger mNextNonce = new AtomicInteger(0);
84
85    private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>();
86
87    private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements
88            Handler.Callback {
89        private static final int MSG_STORAGE_STATE_CHANGED = 1;
90        private static final int MSG_VOLUME_STATE_CHANGED = 2;
91        private static final int MSG_VOLUME_METADATA_CHANGED = 3;
92        private static final int MSG_DISK_UNSUPPORTED = 4;
93
94        final StorageEventListener mCallback;
95        final Handler mHandler;
96
97        public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
98            mCallback = callback;
99            mHandler = new Handler(looper, this);
100        }
101
102        @Override
103        public boolean handleMessage(Message msg) {
104            final SomeArgs args = (SomeArgs) msg.obj;
105            switch (msg.what) {
106                case MSG_STORAGE_STATE_CHANGED:
107                    mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
108                            (String) args.arg3);
109                    args.recycle();
110                    return true;
111                case MSG_VOLUME_STATE_CHANGED:
112                    mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
113                    args.recycle();
114                    return true;
115                case MSG_VOLUME_METADATA_CHANGED:
116                    mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
117                    args.recycle();
118                    return true;
119                case MSG_DISK_UNSUPPORTED:
120                    mCallback.onDiskUnsupported((DiskInfo) args.arg1);
121                    args.recycle();
122                    return true;
123            }
124            args.recycle();
125            return false;
126        }
127
128        @Override
129        public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException {
130            // Ignored
131        }
132
133        @Override
134        public void onStorageStateChanged(String path, String oldState, String newState) {
135            final SomeArgs args = SomeArgs.obtain();
136            args.arg1 = path;
137            args.arg2 = oldState;
138            args.arg3 = newState;
139            mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget();
140        }
141
142        @Override
143        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
144            final SomeArgs args = SomeArgs.obtain();
145            args.arg1 = vol;
146            args.argi2 = oldState;
147            args.argi3 = newState;
148            mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
149        }
150
151        @Override
152        public void onVolumeMetadataChanged(VolumeInfo vol) {
153            final SomeArgs args = SomeArgs.obtain();
154            args.arg1 = vol;
155            mHandler.obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
156        }
157
158        @Override
159        public void onDiskUnsupported(DiskInfo disk) {
160            final SomeArgs args = SomeArgs.obtain();
161            args.arg1 = disk;
162            mHandler.obtainMessage(MSG_DISK_UNSUPPORTED, args).sendToTarget();
163        }
164    }
165
166    /**
167     * Binder listener for OBB action results.
168     */
169    private final ObbActionListener mObbActionListener = new ObbActionListener();
170
171    private class ObbActionListener extends IObbActionListener.Stub {
172        @SuppressWarnings("hiding")
173        private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
174
175        @Override
176        public void onObbResult(String filename, int nonce, int status) {
177            final ObbListenerDelegate delegate;
178            synchronized (mListeners) {
179                delegate = mListeners.get(nonce);
180                if (delegate != null) {
181                    mListeners.remove(nonce);
182                }
183            }
184
185            if (delegate != null) {
186                delegate.sendObbStateChanged(filename, status);
187            }
188        }
189
190        public int addListener(OnObbStateChangeListener listener) {
191            final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
192
193            synchronized (mListeners) {
194                mListeners.put(delegate.nonce, delegate);
195            }
196
197            return delegate.nonce;
198        }
199    }
200
201    private int getNextNonce() {
202        return mNextNonce.getAndIncrement();
203    }
204
205    /**
206     * Private class containing sender and receiver code for StorageEvents.
207     */
208    private class ObbListenerDelegate {
209        private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
210        private final Handler mHandler;
211
212        private final int nonce;
213
214        ObbListenerDelegate(OnObbStateChangeListener listener) {
215            nonce = getNextNonce();
216            mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
217            mHandler = new Handler(mLooper) {
218                @Override
219                public void handleMessage(Message msg) {
220                    final OnObbStateChangeListener changeListener = getListener();
221                    if (changeListener == null) {
222                        return;
223                    }
224
225                    changeListener.onObbStateChange((String) msg.obj, msg.arg1);
226                }
227            };
228        }
229
230        OnObbStateChangeListener getListener() {
231            if (mObbEventListenerRef == null) {
232                return null;
233            }
234            return mObbEventListenerRef.get();
235        }
236
237        void sendObbStateChanged(String path, int state) {
238            mHandler.obtainMessage(0, state, 0, path).sendToTarget();
239        }
240    }
241
242    /** {@hide} */
243    public static StorageManager from(Context context) {
244        return (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
245    }
246
247    /**
248     * Constructs a StorageManager object through which an application can
249     * can communicate with the systems mount service.
250     *
251     * @param tgtLooper The {@link android.os.Looper} which events will be received on.
252     *
253     * <p>Applications can get instance of this class by calling
254     * {@link android.content.Context#getSystemService(java.lang.String)} with an argument
255     * of {@link android.content.Context#STORAGE_SERVICE}.
256     *
257     * @hide
258     */
259    public StorageManager(Context context, Looper looper) {
260        mContext = context;
261        mResolver = context.getContentResolver();
262        mLooper = looper;
263        mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
264        if (mMountService == null) {
265            throw new IllegalStateException("Failed to find running mount service");
266        }
267    }
268
269    /**
270     * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}.
271     *
272     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
273     *
274     * @hide
275     */
276    public void registerListener(StorageEventListener listener) {
277        synchronized (mDelegates) {
278            final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener,
279                    mLooper);
280            try {
281                mMountService.registerListener(delegate);
282            } catch (RemoteException e) {
283                throw e.rethrowAsRuntimeException();
284            }
285            mDelegates.add(delegate);
286        }
287    }
288
289    /**
290     * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}.
291     *
292     * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
293     *
294     * @hide
295     */
296    public void unregisterListener(StorageEventListener listener) {
297        synchronized (mDelegates) {
298            for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) {
299                final StorageEventListenerDelegate delegate = i.next();
300                if (delegate.mCallback == listener) {
301                    try {
302                        mMountService.unregisterListener(delegate);
303                    } catch (RemoteException e) {
304                        throw e.rethrowAsRuntimeException();
305                    }
306                    i.remove();
307                }
308            }
309        }
310    }
311
312    /**
313     * Enables USB Mass Storage (UMS) on the device.
314     *
315     * @hide
316     */
317    @Deprecated
318    public void enableUsbMassStorage() {
319    }
320
321    /**
322     * Disables USB Mass Storage (UMS) on the device.
323     *
324     * @hide
325     */
326    @Deprecated
327    public void disableUsbMassStorage() {
328    }
329
330    /**
331     * Query if a USB Mass Storage (UMS) host is connected.
332     * @return true if UMS host is connected.
333     *
334     * @hide
335     */
336    @Deprecated
337    public boolean isUsbMassStorageConnected() {
338        return false;
339    }
340
341    /**
342     * Query if a USB Mass Storage (UMS) is enabled on the device.
343     * @return true if UMS host is enabled.
344     *
345     * @hide
346     */
347    @Deprecated
348    public boolean isUsbMassStorageEnabled() {
349        return false;
350    }
351
352    /**
353     * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
354     * specified, it is supplied to the mounting process to be used in any
355     * encryption used in the OBB.
356     * <p>
357     * The OBB will remain mounted for as long as the StorageManager reference
358     * is held by the application. As soon as this reference is lost, the OBBs
359     * in use will be unmounted. The {@link OnObbStateChangeListener} registered
360     * with this call will receive the success or failure of this operation.
361     * <p>
362     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
363     * file matches a package ID that is owned by the calling program's UID.
364     * That is, shared UID applications can attempt to mount any other
365     * application's OBB that shares its UID.
366     *
367     * @param rawPath the path to the OBB file
368     * @param key secret used to encrypt the OBB; may be <code>null</code> if no
369     *            encryption was used on the OBB.
370     * @param listener will receive the success or failure of the operation
371     * @return whether the mount call was successfully queued or not
372     */
373    public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
374        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
375        Preconditions.checkNotNull(listener, "listener cannot be null");
376
377        try {
378            final String canonicalPath = new File(rawPath).getCanonicalPath();
379            final int nonce = mObbActionListener.addListener(listener);
380            mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
381            return true;
382        } catch (IOException e) {
383            throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
384        } catch (RemoteException e) {
385            Log.e(TAG, "Failed to mount OBB", e);
386        }
387
388        return false;
389    }
390
391    /**
392     * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
393     * <code>force</code> flag is true, it will kill any application needed to
394     * unmount the given OBB (even the calling application).
395     * <p>
396     * The {@link OnObbStateChangeListener} registered with this call will
397     * receive the success or failure of this operation.
398     * <p>
399     * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
400     * file matches a package ID that is owned by the calling program's UID.
401     * That is, shared UID applications can obtain access to any other
402     * application's OBB that shares its UID.
403     * <p>
404     *
405     * @param rawPath path to the OBB file
406     * @param force whether to kill any programs using this in order to unmount
407     *            it
408     * @param listener will receive the success or failure of the operation
409     * @return whether the unmount call was successfully queued or not
410     */
411    public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
412        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
413        Preconditions.checkNotNull(listener, "listener cannot be null");
414
415        try {
416            final int nonce = mObbActionListener.addListener(listener);
417            mMountService.unmountObb(rawPath, force, mObbActionListener, nonce);
418            return true;
419        } catch (RemoteException e) {
420            Log.e(TAG, "Failed to mount OBB", e);
421        }
422
423        return false;
424    }
425
426    /**
427     * Check whether an Opaque Binary Blob (OBB) is mounted or not.
428     *
429     * @param rawPath path to OBB image
430     * @return true if OBB is mounted; false if not mounted or on error
431     */
432    public boolean isObbMounted(String rawPath) {
433        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
434
435        try {
436            return mMountService.isObbMounted(rawPath);
437        } catch (RemoteException e) {
438            Log.e(TAG, "Failed to check if OBB is mounted", e);
439        }
440
441        return false;
442    }
443
444    /**
445     * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
446     * give you the path to where you can obtain access to the internals of the
447     * OBB.
448     *
449     * @param rawPath path to OBB image
450     * @return absolute path to mounted OBB image data or <code>null</code> if
451     *         not mounted or exception encountered trying to read status
452     */
453    public String getMountedObbPath(String rawPath) {
454        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
455
456        try {
457            return mMountService.getMountedObbPath(rawPath);
458        } catch (RemoteException e) {
459            Log.e(TAG, "Failed to find mounted path for OBB", e);
460        }
461
462        return null;
463    }
464
465    /** {@hide} */
466    public @NonNull List<DiskInfo> getDisks() {
467        try {
468            return Arrays.asList(mMountService.getDisks());
469        } catch (RemoteException e) {
470            throw e.rethrowAsRuntimeException();
471        }
472    }
473
474    /** {@hide} */
475    public @Nullable DiskInfo findDiskById(String id) {
476        Preconditions.checkNotNull(id);
477        // TODO; go directly to service to make this faster
478        for (DiskInfo disk : getDisks()) {
479            if (Objects.equals(disk.id, id)) {
480                return disk;
481            }
482        }
483        return null;
484    }
485
486    /** {@hide} */
487    public @Nullable VolumeInfo findVolumeById(String id) {
488        Preconditions.checkNotNull(id);
489        // TODO; go directly to service to make this faster
490        for (VolumeInfo vol : getVolumes()) {
491            if (Objects.equals(vol.id, id)) {
492                return vol;
493            }
494        }
495        return null;
496    }
497
498    /** {@hide} */
499    public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
500        Preconditions.checkNotNull(fsUuid);
501        // TODO; go directly to service to make this faster
502        for (VolumeInfo vol : getVolumes()) {
503            if (Objects.equals(vol.fsUuid, fsUuid)) {
504                return vol;
505            }
506        }
507        return null;
508    }
509
510    /** {@hide} */
511    public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
512        return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
513    }
514
515    /** {@hide} */
516    public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) {
517        return findVolumeById(privateVol.getId().replace("private", "emulated"));
518    }
519
520    /** {@hide} */
521    public @NonNull List<VolumeInfo> getVolumes() {
522        return getVolumes(0);
523    }
524
525    /** {@hide} */
526    public @NonNull List<VolumeInfo> getVolumes(int flags) {
527        try {
528            return Arrays.asList(mMountService.getVolumes(flags));
529        } catch (RemoteException e) {
530            throw e.rethrowAsRuntimeException();
531        }
532    }
533
534    /** {@hide} */
535    public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
536        String descrip = vol.getDescription();
537
538        if (vol.disk != null) {
539            if (TextUtils.isEmpty(descrip)) {
540                descrip = vol.disk.getDescription();
541            }
542        }
543
544        return descrip;
545    }
546
547    /** {@hide} */
548    public void mount(String volId) {
549        try {
550            mMountService.mount(volId);
551        } catch (RemoteException e) {
552            throw e.rethrowAsRuntimeException();
553        }
554    }
555
556    /** {@hide} */
557    public void unmount(String volId) {
558        try {
559            mMountService.unmount(volId);
560        } catch (RemoteException e) {
561            throw e.rethrowAsRuntimeException();
562        }
563    }
564
565    /** {@hide} */
566    public void format(String volId) {
567        try {
568            mMountService.format(volId);
569        } catch (RemoteException e) {
570            throw e.rethrowAsRuntimeException();
571        }
572    }
573
574    /** {@hide} */
575    public void partitionPublic(String diskId) {
576        try {
577            mMountService.partitionPublic(diskId);
578        } catch (RemoteException e) {
579            throw e.rethrowAsRuntimeException();
580        }
581    }
582
583    /** {@hide} */
584    public void partitionPrivate(String diskId) {
585        try {
586            mMountService.partitionPrivate(diskId);
587        } catch (RemoteException e) {
588            throw e.rethrowAsRuntimeException();
589        }
590    }
591
592    /** {@hide} */
593    public void partitionMixed(String diskId, int ratio) {
594        try {
595            mMountService.partitionMixed(diskId, ratio);
596        } catch (RemoteException e) {
597            throw e.rethrowAsRuntimeException();
598        }
599    }
600
601    /** {@hide} */
602    public void setVolumeNickname(String volId, String nickname) {
603        try {
604            mMountService.setVolumeNickname(volId, nickname);
605        } catch (RemoteException e) {
606            throw e.rethrowAsRuntimeException();
607        }
608    }
609
610    /** {@hide} */
611    public void setVolumeInited(String volId, boolean inited) {
612        try {
613            mMountService.setVolumeUserFlags(volId, inited ? VolumeInfo.USER_FLAG_INITED : 0,
614                    VolumeInfo.USER_FLAG_INITED);
615        } catch (RemoteException e) {
616            throw e.rethrowAsRuntimeException();
617        }
618    }
619
620    /** {@hide} */
621    public void setVolumeSnoozed(String volId, boolean snoozed) {
622        try {
623            mMountService.setVolumeUserFlags(volId, snoozed ? VolumeInfo.USER_FLAG_SNOOZED : 0,
624                    VolumeInfo.USER_FLAG_SNOOZED);
625        } catch (RemoteException e) {
626            throw e.rethrowAsRuntimeException();
627        }
628    }
629
630    /** {@hide} */
631    public @Nullable StorageVolume getStorageVolume(File file) {
632        return getStorageVolume(getVolumeList(), file);
633    }
634
635    /** {@hide} */
636    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
637        return getStorageVolume(getVolumeList(userId), file);
638    }
639
640    /** {@hide} */
641    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
642        File canonicalFile = null;
643        try {
644            canonicalFile = file.getCanonicalFile();
645        } catch (IOException ignored) {
646            canonicalFile = null;
647        }
648        for (StorageVolume volume : volumes) {
649            if (volume.getPathFile().equals(file)) {
650                return volume;
651            }
652            if (FileUtils.contains(volume.getPathFile(), canonicalFile)) {
653                return volume;
654            }
655        }
656        return null;
657    }
658
659    /**
660     * Gets the state of a volume via its mountpoint.
661     * @hide
662     */
663    @Deprecated
664    public @NonNull String getVolumeState(String mountPoint) {
665        final StorageVolume vol = getStorageVolume(new File(mountPoint));
666        if (vol != null) {
667            return vol.getState();
668        } else {
669            return Environment.MEDIA_UNKNOWN;
670        }
671    }
672
673    /** {@hide} */
674    public @NonNull StorageVolume[] getVolumeList() {
675        return getVolumeList(mContext.getUserId());
676    }
677
678    /** {@hide} */
679    public static @NonNull StorageVolume[] getVolumeList(int userId) {
680        final IMountService mountService = IMountService.Stub.asInterface(
681                ServiceManager.getService("mount"));
682        try {
683            return mountService.getVolumeList(userId);
684        } catch (RemoteException e) {
685            throw e.rethrowAsRuntimeException();
686        }
687    }
688
689    /**
690     * Returns list of paths for all mountable volumes.
691     * @hide
692     */
693    @Deprecated
694    public @NonNull String[] getVolumePaths() {
695        StorageVolume[] volumes = getVolumeList();
696        int count = volumes.length;
697        String[] paths = new String[count];
698        for (int i = 0; i < count; i++) {
699            paths[i] = volumes[i].getPath();
700        }
701        return paths;
702    }
703
704    /** {@hide} */
705    public @NonNull StorageVolume getPrimaryVolume() {
706        return getPrimaryVolume(getVolumeList());
707    }
708
709    /** {@hide} */
710    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
711        for (StorageVolume volume : volumes) {
712            if (volume.isPrimary()) {
713                return volume;
714            }
715        }
716        throw new IllegalStateException("Missing primary storage");
717    }
718
719    /** {@hide} */
720    private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
721    private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
722    private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
723
724    /**
725     * Return the number of available bytes until the given path is considered
726     * running low on storage.
727     *
728     * @hide
729     */
730    public long getStorageBytesUntilLow(File path) {
731        return path.getUsableSpace() - getStorageFullBytes(path);
732    }
733
734    /**
735     * Return the number of available bytes at which the given path is
736     * considered running low on storage.
737     *
738     * @hide
739     */
740    public long getStorageLowBytes(File path) {
741        final long lowPercent = Settings.Global.getInt(mResolver,
742                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
743        final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
744
745        final long maxLowBytes = Settings.Global.getLong(mResolver,
746                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);
747
748        return Math.min(lowBytes, maxLowBytes);
749    }
750
751    /**
752     * Return the number of available bytes at which the given path is
753     * considered full.
754     *
755     * @hide
756     */
757    public long getStorageFullBytes(File path) {
758        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
759                DEFAULT_FULL_THRESHOLD_BYTES);
760    }
761
762    /// Consts to match the password types in cryptfs.h
763    /** @hide */
764    public static final int CRYPT_TYPE_PASSWORD = 0;
765    /** @hide */
766    public static final int CRYPT_TYPE_DEFAULT = 1;
767    /** @hide */
768    public static final int CRYPT_TYPE_PATTERN = 2;
769    /** @hide */
770    public static final int CRYPT_TYPE_PIN = 3;
771
772    // Constants for the data available via MountService.getField.
773    /** @hide */
774    public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
775    /** @hide */
776    public static final String OWNER_INFO_KEY = "OwnerInfo";
777    /** @hide */
778    public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
779}
780