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