Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e83cefce authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

New external storage APIs.

This implements the spec for external storage organization, and
properly reflects how the media scanner organizes the files it finds.

Also includes package manager support for removing app private
files from external storage when the application is uninstalled.

For the new APIs and paths, the main place to look is Environment
and Context.
parent 72e5a882
Loading
Loading
Loading
Loading
+219 −0
Original line number Diff line number Diff line
@@ -32785,6 +32785,30 @@
<parameter name="mode" type="int">
</parameter>
</method>
<method name="getExternalCacheDir"
 return="java.io.File"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getExternalFilesDir"
 return="java.io.File"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="type" type="java.lang.String">
</parameter>
</method>
<method name="getFileStreamPath"
 return="java.io.File"
 abstract="true"
@@ -34293,6 +34317,30 @@
<parameter name="mode" type="int">
</parameter>
</method>
<method name="getExternalCacheDir"
 return="java.io.File"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getExternalFilesDir"
 return="java.io.File"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="type" type="java.lang.String">
</parameter>
</method>
<method name="getFileStreamPath"
 return="java.io.File"
 abstract="false"
@@ -83028,6 +83076,25 @@
<parameter name="mimeType" type="java.lang.String">
</parameter>
</method>
<method name="scanFile"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="context" type="android.content.Context">
</parameter>
<parameter name="paths" type="java.lang.String[]">
</parameter>
<parameter name="mimeTypes" type="java.lang.String[]">
</parameter>
<parameter name="callback" type="android.media.MediaScannerConnection.ScanResultListener">
</parameter>
</method>
</class>
<interface name="MediaScannerConnection.MediaScannerConnectionClient"
 abstract="true"
@@ -83036,6 +83103,8 @@
 deprecated="not deprecated"
 visibility="public"
>
<implements name="android.media.MediaScannerConnection.ScanResultListener">
</implements>
<method name="onMediaScannerConnected"
 return="void"
 abstract="true"
@@ -83063,6 +83132,29 @@
</parameter>
</method>
</interface>
<interface name="MediaScannerConnection.ScanResultListener"
 abstract="true"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<method name="onScanCompleted"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="path" type="java.lang.String">
</parameter>
<parameter name="uri" type="android.net.Uri">
</parameter>
</method>
</interface>
<class name="Ringtone"
 extends="java.lang.Object"
 abstract="false"
@@ -112670,6 +112762,19 @@
 visibility="public"
>
</method>
<method name="getExternalStoragePublicDirectory"
 return="java.io.File"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="type" type="java.lang.String">
</parameter>
</method>
<method name="getExternalStorageState"
 return="java.lang.String"
 abstract="false"
@@ -112692,6 +112797,96 @@
 visibility="public"
>
</method>
<field name="DIRECTORY_ALARMS"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DIRECTORY_DCIM"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DIRECTORY_DOWNLOADS"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DIRECTORY_MOVIES"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DIRECTORY_MUSIC"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DIRECTORY_NOTIFICATIONS"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DIRECTORY_PICTURES"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DIRECTORY_PODCASTS"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DIRECTORY_RINGTONES"
 type="java.lang.String"
 transient="false"
 volatile="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="MEDIA_BAD_REMOVAL"
 type="java.lang.String"
 transient="false"
@@ -144243,6 +144438,30 @@
<parameter name="mode" type="int">
</parameter>
</method>
<method name="getExternalCacheDir"
 return="java.io.File"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getExternalFilesDir"
 return="java.io.File"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="type" type="java.lang.String">
</parameter>
</method>
<method name="getFileStreamPath"
 return="java.io.File"
 abstract="false"
+56 −3
Original line number Diff line number Diff line
@@ -197,9 +197,9 @@ class ContextImpl extends Context {
    private File mDatabasesDir;
    private File mPreferencesDir;
    private File mFilesDir;
    

    private File mCacheDir;
    private File mExternalFilesDir;
    private File mExternalCacheDir;
    
    private static long sInstanceCount = 0;

@@ -437,6 +437,38 @@ class ContextImpl extends Context {
        }
    }
    
    @Override
    public File getExternalFilesDir(String type) {
        synchronized (mSync) {
            if (mExternalFilesDir == null) {
                mExternalFilesDir = Environment.getExternalStorageAppFilesDirectory(
                        getPackageName());
            }
            if (!mExternalFilesDir.exists()) {
                try {
                    (new File(Environment.getExternalStorageAndroidDataDir(),
                            ".nomedia")).createNewFile();
                } catch (IOException e) {
                }
                if (!mExternalFilesDir.mkdirs()) {
                    Log.w(TAG, "Unable to create external files directory");
                    return null;
                }
            }
            if (type == null) {
                return mExternalFilesDir;
            }
            File dir = new File(mExternalFilesDir, type);
            if (!dir.exists()) {
                if (!dir.mkdirs()) {
                    Log.w(TAG, "Unable to create external media directory " + dir);
                    return null;
                }
            }
            return dir;
        }
    }
    
    @Override
    public File getCacheDir() {
        synchronized (mSync) {
@@ -457,6 +489,27 @@ class ContextImpl extends Context {
        return mCacheDir;
    }
    
    @Override
    public File getExternalCacheDir() {
        synchronized (mSync) {
            if (mExternalCacheDir == null) {
                mExternalCacheDir = Environment.getExternalStorageAppCacheDirectory(
                        getPackageName());
            }
            if (!mExternalCacheDir.exists()) {
                try {
                    (new File(Environment.getExternalStorageAndroidDataDir(),
                            ".nomedia")).createNewFile();
                } catch (IOException e) {
                }
                if (!mExternalCacheDir.mkdirs()) {
                    Log.w(TAG, "Unable to create external cache directory");
                    return null;
                }
            }
            return mExternalCacheDir;
        }
    }
    
    @Override
    public File getFileStreamPath(String name) {
+1 −1
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ public abstract class IntentService extends Service {
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_REDELIVER_INTENT} instead of
     * {@link Service#START_NOT_STICKY}, so that if this service's process
     * is called while it is executing the Intent in
     * is killed while it is executing the Intent in
     * {@link #onHandleIntent(Intent)}, then when later restarted the same Intent
     * will be re-delivered to it, to retry its execution.
     */
+129 −3
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.media.MediaScannerConnection.ScanResultListener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -137,7 +138,28 @@ public abstract class Context {
    
    /**
     * Return the context of the single, global Application object of the
     * current process.
     * current process.  This generally should only be used if you need a
     * Context whose lifecycle is separate from the current context, that is
     * tied to the lifetime of the process rather than the current component.
     * 
     * <p>Consider for example how this interacts with
     * {@ #registerReceiver(BroadcastReceiver, IntentFilter)}:
     * <ul>
     * <li> <p>If used from an Activity context, the receiver is being registered
     * within that activity.  This means that you are expected to unregister
     * before the activity is done being destroyed; in fact if you do not do
     * so, the framework will clean up your leaked registration as it removes
     * the activity and log an error.  Thus, if you use the Activity context
     * to register a receiver that is static (global to the process, not
     * associated with an Activity instance) then that registration will be
     * removed on you at whatever point the activity you used is destroyed.
     * <li> <p>If used from the Context returned here, the receiver is being
     * registered with the global state associated with your application.  Thus
     * it will never be unregistered for you.  This is necessary if the receiver
     * is associated with static data, not a particular component.  However
     * using the ApplicationContext elsewhere can easily lead to serious leaks
     * if you forget to unregister, unbind, etc.
     * </ul>
     */
    public abstract Context getApplicationContext();

@@ -392,12 +414,85 @@ public abstract class Context {
     */
    public abstract File getFilesDir();
    
    /**
     * Returns the absolute path to the directory on the external filesystem
     * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
     * Environment.getExternalStorageDirectory()} where the application can
     * place persistent files it owns.  These files are private to the
     * applications, and not typically visible to the user as media.
     * 
     * <p>This is like {@link #getFilesDir()} in that these
     * files will be deleted when the application is uninstalled, however there
     * are some important differences:
     * 
     * <ul>
     * <li>External files are not always available: they will disappear if the
     * user mounts the external storage on a computer or removes it.  See the
     * APIs on {@link android.os.Environment} for information in the storage state.
     * <li>There is no security enforced with these files.  All applications
     * can read and write files placed here.
     * </ul>
     * 
     * <p>Here is an example of typical code to manipulate a file in
     * an application's private storage:</p>
     * 
     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
     * private_file}
     *
     * <p>If you install a non-null <var>type</var> to this function, the returned
     * file will be a path to a sub-directory of the given type.  Though these files
     * are not automatically scanned by the media scanner, you can explicitly
     * add them to the media database with
     * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[],
     *      ScanResultListener) MediaScannerConnection.scanFile}.
     * Note that this is not the same as
     * {@link android.os.Environment#getExternalStoragePublicDirectory
     * Environment.getExternalStoragePublicDirectory()}, which provides
     * directories of media shared by all applications.  The
     * directories returned here are
     * owned by the application, and its contents will be removed when the
     * application is uninstalled.  Unlike
     * {@link android.os.Environment#getExternalStoragePublicDirectory
     * Environment.getExternalStoragePublicDirectory()}, the directory
     * returned here will be automatically created for you.
     * 
     * <p>Here is an example of typical code to manipulate a picture in
     * an application's private storage and add it to the media database:</p>
     * 
     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
     * private_picture}
     * 
     * @param type The type of files directory to return.  May be null for
     * the root of the files directory or one of
     * the following Environment constants for a subdirectory:
     * {@link android.os.Environment#DIRECTORY_MUSIC},
     * {@link android.os.Environment#DIRECTORY_PODCASTS},
     * {@link android.os.Environment#DIRECTORY_RINGTONES},
     * {@link android.os.Environment#DIRECTORY_ALARMS},
     * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
     * {@link android.os.Environment#DIRECTORY_PICTURES}, or
     * {@link android.os.Environment#DIRECTORY_MOVIES}.
     * 
     * @return Returns the path of the directory holding application files
     * on external storage.  Returns null if external storage is not currently
     * mounted so it could not ensure the path exists; you will need to call
     * this method again when it is available.
     *
     * @see #getFilesDir
     */
    public abstract File getExternalFilesDir(String type);
    
    /**
     * Returns the absolute path to the application specific cache directory 
     * on the filesystem. These files will be ones that get deleted first when the
     * device runs low on storage
     * device runs low on storage.
     * There is no guarantee when these files will be deleted.
     * 
     * <strong>Note: you should not <em>rely</em> on the system deleting these
     * files for you; you should always have a reasonable maximum, such as 1 MB,
     * for the amount of space you consume with cache files, and prune those
     * files when exceeding that space.</strong>
     * 
     * @return Returns the path of the directory holding application cache files.
     *
     * @see #openFileOutput
@@ -406,6 +501,37 @@ public abstract class Context {
     */
    public abstract File getCacheDir();

    /**
     * Returns the absolute path to the directory on the external filesystem
     * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
     * Environment.getExternalStorageDirectory()} where the application can
     * place cache files it owns.
     * 
     * <p>This is like {@link #getCacheDir()} in that these
     * files will be deleted when the application is uninstalled, however there
     * are some important differences:
     * 
     * <ul>
     * <li>The platform does not monitor the space available in external storage,
     * and thus will not automatically delete these files.  Note that you should
     * be managing the maximum space you will use for these anyway, just like
     * with {@link #getCacheDir()}.
     * <li>External files are not always available: they will disappear if the
     * user mounts the external storage on a computer or removes it.  See the
     * APIs on {@link android.os.Environment} for information in the storage state.
     * <li>There is no security enforced with these files.  All applications
     * can read and write files placed here.
     * </ul>
     *
     * @return Returns the path of the directory holding application cache files
     * on external storage.  Returns null if external storage is not currently
     * mounted so it could not ensure the path exists; you will need to call
     * this method again when it is available.
     *
     * @see #getCacheDir
     */
    public abstract File getExternalCacheDir();
    
    /**
     * Returns an array of strings naming the private files associated with
     * this Context's application package.
+10 −0
Original line number Diff line number Diff line
@@ -178,11 +178,21 @@ public class ContextWrapper extends Context {
        return mBase.getFilesDir();
    }
    
    @Override
    public File getExternalFilesDir(String type) {
        return mBase.getExternalFilesDir(type);
    }
    
    @Override
    public File getCacheDir() {
        return mBase.getCacheDir();
    }

    @Override
    public File getExternalCacheDir() {
        return mBase.getExternalCacheDir();
    }

    @Override
    public File getDir(String name, int mode) {
        return mBase.getDir(name, mode);
Loading