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