summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt12
-rw-r--r--api/system-current.txt12
-rw-r--r--api/test-current.txt12
-rw-r--r--core/java/android/app/DownloadManager.java48
-rw-r--r--core/java/android/app/SystemServiceRegistry.java2
-rw-r--r--core/java/android/content/ContentProviderClient.java106
-rw-r--r--core/java/android/content/ContentResolver.java44
-rw-r--r--core/java/android/provider/MediaStore.java43
-rw-r--r--drm/java/android/drm/DrmManagerClient.java65
-rw-r--r--media/java/android/media/MediaInserter.java10
-rw-r--r--media/java/android/media/MediaScanner.java203
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java91
-rw-r--r--media/java/android/mtp/MtpPropertyGroup.java24
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java6
14 files changed, 396 insertions, 282 deletions
diff --git a/api/current.txt b/api/current.txt
index 94bb21a4b362..53db7b2d9ea7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4228,7 +4228,7 @@ package android.app {
field public static final java.lang.String COLUMN_DESCRIPTION = "description";
field public static final java.lang.String COLUMN_ID = "_id";
field public static final java.lang.String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
- field public static final java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
+ field public static final deprecated java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
field public static final java.lang.String COLUMN_LOCAL_URI = "local_uri";
field public static final java.lang.String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
field public static final java.lang.String COLUMN_MEDIA_TYPE = "media_type";
@@ -7568,11 +7568,12 @@ package android.content {
method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
}
- public class ContentProviderClient {
+ public class ContentProviderClient implements java.lang.AutoCloseable {
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
+ method public void close();
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
method public android.content.ContentProvider getLocalContentProvider();
method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -7586,7 +7587,7 @@ package android.content {
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
- method public boolean release();
+ method public deprecated boolean release();
method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
}
@@ -10844,7 +10845,7 @@ package android.drm {
field public final int statusCode;
}
- public class DrmManagerClient {
+ public class DrmManagerClient implements java.lang.AutoCloseable {
ctor public DrmManagerClient(android.content.Context);
method public android.drm.DrmInfo acquireDrmInfo(android.drm.DrmInfoRequest);
method public int acquireRights(android.drm.DrmInfoRequest);
@@ -10854,6 +10855,7 @@ package android.drm {
method public int checkRightsStatus(android.net.Uri);
method public int checkRightsStatus(java.lang.String, int);
method public int checkRightsStatus(android.net.Uri, int);
+ method public void close();
method public android.drm.DrmConvertedStatus closeConvertSession(int);
method public android.drm.DrmConvertedStatus convertData(int, byte[]);
method public java.lang.String[] getAvailableDrmEngines();
@@ -10867,7 +10869,7 @@ package android.drm {
method public java.lang.String getOriginalMimeType(android.net.Uri);
method public int openConvertSession(java.lang.String);
method public int processDrmInfo(android.drm.DrmInfo);
- method public void release();
+ method public deprecated void release();
method public int removeAllRights();
method public int removeRights(java.lang.String);
method public int removeRights(android.net.Uri);
diff --git a/api/system-current.txt b/api/system-current.txt
index 79e44ea73727..967e6c800ea1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4348,7 +4348,7 @@ package android.app {
field public static final java.lang.String COLUMN_DESCRIPTION = "description";
field public static final java.lang.String COLUMN_ID = "_id";
field public static final java.lang.String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
- field public static final java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
+ field public static final deprecated java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
field public static final java.lang.String COLUMN_LOCAL_URI = "local_uri";
field public static final java.lang.String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
field public static final java.lang.String COLUMN_MEDIA_TYPE = "media_type";
@@ -7811,11 +7811,12 @@ package android.content {
method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
}
- public class ContentProviderClient {
+ public class ContentProviderClient implements java.lang.AutoCloseable {
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
+ method public void close();
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
method public android.content.ContentProvider getLocalContentProvider();
method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -7829,7 +7830,7 @@ package android.content {
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
- method public boolean release();
+ method public deprecated boolean release();
method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
}
@@ -11198,7 +11199,7 @@ package android.drm {
field public final int statusCode;
}
- public class DrmManagerClient {
+ public class DrmManagerClient implements java.lang.AutoCloseable {
ctor public DrmManagerClient(android.content.Context);
method public android.drm.DrmInfo acquireDrmInfo(android.drm.DrmInfoRequest);
method public int acquireRights(android.drm.DrmInfoRequest);
@@ -11208,6 +11209,7 @@ package android.drm {
method public int checkRightsStatus(android.net.Uri);
method public int checkRightsStatus(java.lang.String, int);
method public int checkRightsStatus(android.net.Uri, int);
+ method public void close();
method public android.drm.DrmConvertedStatus closeConvertSession(int);
method public android.drm.DrmConvertedStatus convertData(int, byte[]);
method public java.lang.String[] getAvailableDrmEngines();
@@ -11221,7 +11223,7 @@ package android.drm {
method public java.lang.String getOriginalMimeType(android.net.Uri);
method public int openConvertSession(java.lang.String);
method public int processDrmInfo(android.drm.DrmInfo);
- method public void release();
+ method public deprecated void release();
method public int removeAllRights();
method public int removeRights(java.lang.String);
method public int removeRights(android.net.Uri);
diff --git a/api/test-current.txt b/api/test-current.txt
index 090644bd3dc6..66065083e3cf 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4228,7 +4228,7 @@ package android.app {
field public static final java.lang.String COLUMN_DESCRIPTION = "description";
field public static final java.lang.String COLUMN_ID = "_id";
field public static final java.lang.String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
- field public static final java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
+ field public static final deprecated java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
field public static final java.lang.String COLUMN_LOCAL_URI = "local_uri";
field public static final java.lang.String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
field public static final java.lang.String COLUMN_MEDIA_TYPE = "media_type";
@@ -7568,11 +7568,12 @@ package android.content {
method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
}
- public class ContentProviderClient {
+ public class ContentProviderClient implements java.lang.AutoCloseable {
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
+ method public void close();
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
method public android.content.ContentProvider getLocalContentProvider();
method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -7586,7 +7587,7 @@ package android.content {
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
- method public boolean release();
+ method public deprecated boolean release();
method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
}
@@ -10844,7 +10845,7 @@ package android.drm {
field public final int statusCode;
}
- public class DrmManagerClient {
+ public class DrmManagerClient implements java.lang.AutoCloseable {
ctor public DrmManagerClient(android.content.Context);
method public android.drm.DrmInfo acquireDrmInfo(android.drm.DrmInfoRequest);
method public int acquireRights(android.drm.DrmInfoRequest);
@@ -10854,6 +10855,7 @@ package android.drm {
method public int checkRightsStatus(android.net.Uri);
method public int checkRightsStatus(java.lang.String, int);
method public int checkRightsStatus(android.net.Uri, int);
+ method public void close();
method public android.drm.DrmConvertedStatus closeConvertSession(int);
method public android.drm.DrmConvertedStatus convertData(int, byte[]);
method public java.lang.String[] getAvailableDrmEngines();
@@ -10867,7 +10869,7 @@ package android.drm {
method public java.lang.String getOriginalMimeType(android.net.Uri);
method public int openConvertSession(java.lang.String);
method public int processDrmInfo(android.drm.DrmInfo);
- method public void release();
+ method public deprecated void release();
method public int removeAllRights();
method public int removeRights(java.lang.String);
method public int removeRights(android.net.Uri);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index fb0e79b849b2..a9516d03ba0d 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -27,6 +27,7 @@ import android.database.CursorWrapper;
import android.net.ConnectivityManager;
import android.net.NetworkPolicyManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.Downloads;
@@ -105,8 +106,17 @@ public class DownloadManager {
public final static String COLUMN_LOCAL_URI = "local_uri";
/**
- * The pathname of the file where the download is stored.
+ * Path to the downloaded file on disk.
+ * <p>
+ * Note that apps may not have filesystem permissions to directly access
+ * this path. Instead of trying to open this path directly, apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain access.
+ *
+ * @deprecated apps should transition to using
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}
+ * instead.
*/
+ @Deprecated
public final static String COLUMN_LOCAL_FILENAME = "local_filename";
/**
@@ -908,16 +918,19 @@ public class DownloadManager {
}
}
- private ContentResolver mResolver;
- private String mPackageName;
+ private final ContentResolver mResolver;
+ private final String mPackageName;
+ private final int mTargetSdkVersion;
+
private Uri mBaseUri = Downloads.Impl.CONTENT_URI;
/**
* @hide
*/
- public DownloadManager(ContentResolver resolver, String packageName) {
- mResolver = resolver;
- mPackageName = packageName;
+ public DownloadManager(Context context) {
+ mResolver = context.getContentResolver();
+ mPackageName = context.getPackageName();
+ mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
}
/**
@@ -997,7 +1010,7 @@ public class DownloadManager {
if (underlyingCursor == null) {
return null;
}
- return new CursorTranslator(underlyingCursor, mBaseUri);
+ return new CursorTranslator(underlyingCursor, mBaseUri, mTargetSdkVersion);
}
/**
@@ -1265,11 +1278,13 @@ public class DownloadManager {
* underlying data.
*/
private static class CursorTranslator extends CursorWrapper {
- private Uri mBaseUri;
+ private final Uri mBaseUri;
+ private final int mTargetSdkVersion;
- public CursorTranslator(Cursor cursor, Uri baseUri) {
+ public CursorTranslator(Cursor cursor, Uri baseUri, int targetSdkVersion) {
super(cursor);
mBaseUri = baseUri;
+ mTargetSdkVersion = targetSdkVersion;
}
@Override
@@ -1290,8 +1305,19 @@ public class DownloadManager {
@Override
public String getString(int columnIndex) {
- return (getColumnName(columnIndex).equals(COLUMN_LOCAL_URI)) ? getLocalUri() :
- super.getString(columnIndex);
+ final String columnName = getColumnName(columnIndex);
+ switch (columnName) {
+ case COLUMN_LOCAL_URI:
+ return getLocalUri();
+ case COLUMN_LOCAL_FILENAME:
+ if (mTargetSdkVersion >= Build.VERSION_CODES.N) {
+ throw new IllegalArgumentException(
+ "COLUMN_LOCAL_FILENAME is deprecated;"
+ + " use ContentResolver.openFileDescriptor() instead");
+ }
+ default:
+ return super.getString(columnIndex);
+ }
}
private String getLocalUri() {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 288a2cb5fa4f..89d52f248bde 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -247,7 +247,7 @@ final class SystemServiceRegistry {
new CachedServiceFetcher<DownloadManager>() {
@Override
public DownloadManager createService(ContextImpl ctx) {
- return new DownloadManager(ctx.getContentResolver(), ctx.getPackageName());
+ return new DownloadManager(ctx);
}});
registerService(Context.BATTERY_SERVICE, BatteryManager.class,
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index d12595f7ac07..dec0d9107d3a 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -19,6 +19,7 @@ package android.content;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.AssetFileDescriptor;
+import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -32,27 +33,34 @@ import android.os.RemoteException;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import java.io.FileNotFoundException;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
- * The public interface object used to interact with a {@link ContentProvider}. This is obtained by
- * calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
- * using {@link #release} in order to indicate to the system that the {@link ContentProvider} is
- * no longer needed and can be killed to free up resources.
- *
- * <p>Note that you should generally create a new ContentProviderClient instance
- * for each thread that will be performing operations. Unlike
+ * The public interface object used to interact with a specific
+ * {@link ContentProvider}.
+ * <p>
+ * Instances can be obtained by calling
+ * {@link ContentResolver#acquireContentProviderClient} or
+ * {@link ContentResolver#acquireUnstableContentProviderClient}. Instances must
+ * be released using {@link #close()} in order to indicate to the system that
+ * the underlying {@link ContentProvider} is no longer needed and can be killed
+ * to free up resources.
+ * <p>
+ * Note that you should generally create a new ContentProviderClient instance
+ * for each thread that will be performing operations. Unlike
* {@link ContentResolver}, the methods here such as {@link #query} and
- * {@link #openFile} are not thread safe -- you must not call
- * {@link #release()} on the ContentProviderClient those calls are made from
- * until you are finished with the data they have returned.
+ * {@link #openFile} are not thread safe -- you must not call {@link #close()}
+ * on the ContentProviderClient those calls are made from until you are finished
+ * with the data they have returned.
*/
-public class ContentProviderClient {
+public class ContentProviderClient implements AutoCloseable {
private static final String TAG = "ContentProviderClient";
@GuardedBy("ContentProviderClient.class")
@@ -63,22 +71,23 @@ public class ContentProviderClient {
private final String mPackageName;
private final boolean mStable;
- private final CloseGuard mGuard = CloseGuard.get();
+ private final AtomicBoolean mClosed = new AtomicBoolean();
+ private final CloseGuard mCloseGuard = CloseGuard.get();
private long mAnrTimeout;
private NotRespondingRunnable mAnrRunnable;
- private boolean mReleased;
-
/** {@hide} */
- ContentProviderClient(
+ @VisibleForTesting
+ public ContentProviderClient(
ContentResolver contentResolver, IContentProvider contentProvider, boolean stable) {
mContentResolver = contentResolver;
mContentProvider = contentProvider;
mPackageName = contentResolver.mPackageName;
+
mStable = stable;
- mGuard.open("release");
+ mCloseGuard.open("close");
}
/** {@hide} */
@@ -133,8 +142,9 @@ public class ContentProviderClient {
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return mContentProvider.query(mPackageName, url, projection, selection, selectionArgs,
- sortOrder, remoteCancellationSignal);
+ final Cursor cursor = mContentProvider.query(mPackageName, url, projection, selection,
+ selectionArgs, sortOrder, remoteCancellationSignal);
+ return new CursorWrapperInner(cursor);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -446,29 +456,42 @@ public class ContentProviderClient {
}
/**
- * Call this to indicate to the system that the associated {@link ContentProvider} is no
- * longer needed by this {@link ContentProviderClient}.
- * @return true if this was release, false if it was already released
+ * Closes this client connection, indicating to the system that the
+ * underlying {@link ContentProvider} is no longer needed.
*/
+ @Override
+ public void close() {
+ closeInternal();
+ }
+
+ /**
+ * @deprecated replaced by {@link #close()}.
+ */
+ @Deprecated
public boolean release() {
- synchronized (this) {
- if (mReleased) {
- throw new IllegalStateException("Already released");
- }
- mReleased = true;
- mGuard.close();
+ return closeInternal();
+ }
+
+ private boolean closeInternal() {
+ mCloseGuard.close();
+ if (mClosed.compareAndSet(false, true)) {
if (mStable) {
return mContentResolver.releaseProvider(mContentProvider);
} else {
return mContentResolver.releaseUnstableProvider(mContentProvider);
}
+ } else {
+ return false;
}
}
@Override
protected void finalize() throws Throwable {
- if (mGuard != null) {
- mGuard.warnIfOpen();
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
}
}
@@ -502,4 +525,29 @@ public class ContentProviderClient {
mContentResolver.appNotRespondingViaProvider(mContentProvider);
}
}
+
+ private final class CursorWrapperInner extends CrossProcessCursorWrapper {
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ CursorWrapperInner(Cursor cursor) {
+ super(cursor);
+ mCloseGuard.open("close");
+ }
+
+ @Override
+ public void close() {
+ mCloseGuard.close();
+ super.close();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ }
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9d0ebc20bdfb..ce5d3b1f5405 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -47,11 +47,11 @@ import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
-import dalvik.system.CloseGuard;
-
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import dalvik.system.CloseGuard;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -59,9 +59,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class provides applications access to the content model.
@@ -514,8 +514,9 @@ public abstract class ContentResolver {
maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
// Wrap the cursor object into CursorWrapperInner object.
- CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
- stableProvider != null ? stableProvider : acquireProvider(uri));
+ final IContentProvider provider = (stableProvider != null) ? stableProvider
+ : acquireProvider(uri);
+ final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
return wrapper;
@@ -2503,41 +2504,31 @@ public abstract class ContentResolver {
private final class CursorWrapperInner extends CrossProcessCursorWrapper {
private final IContentProvider mContentProvider;
- public static final String TAG="CursorWrapperInner";
+ private final AtomicBoolean mProviderReleased = new AtomicBoolean();
private final CloseGuard mCloseGuard = CloseGuard.get();
- private boolean mProviderReleased;
- CursorWrapperInner(Cursor cursor, IContentProvider icp) {
+ CursorWrapperInner(Cursor cursor, IContentProvider contentProvider) {
super(cursor);
- mContentProvider = icp;
+ mContentProvider = contentProvider;
mCloseGuard.open("close");
}
@Override
public void close() {
+ mCloseGuard.close();
super.close();
- ContentResolver.this.releaseProvider(mContentProvider);
- mProviderReleased = true;
- if (mCloseGuard != null) {
- mCloseGuard.close();
+ if (mProviderReleased.compareAndSet(false, true)) {
+ ContentResolver.this.releaseProvider(mContentProvider);
}
}
@Override
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
-
- if (!mProviderReleased && mContentProvider != null) {
- // Even though we are using CloseGuard, log this anyway so that
- // application developers always see the message in the log.
- Log.w(TAG, "Cursor finalized without prior close()");
- ContentResolver.this.releaseProvider(mContentProvider);
- }
+ mCloseGuard.warnIfOpen();
+ close();
} finally {
super.finalize();
}
@@ -2546,7 +2537,7 @@ public abstract class ContentResolver {
private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
private final IContentProvider mContentProvider;
- private boolean mProviderReleased;
+ private final AtomicBoolean mProviderReleased = new AtomicBoolean();
ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
super(pfd);
@@ -2555,9 +2546,8 @@ public abstract class ContentResolver {
@Override
public void releaseResources() {
- if (!mProviderReleased) {
+ if (mProviderReleased.compareAndSet(false, true)) {
ContentResolver.this.releaseProvider(mContentProvider);
- mProviderReleased = true;
}
}
}
@@ -2584,7 +2574,9 @@ public abstract class ContentResolver {
private static IContentService sContentService;
private final Context mContext;
+
final String mPackageName;
+
private static final String TAG = "ContentResolver";
/** @hide */
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 48b3c1a1b064..89ac27c9a816 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -384,8 +384,14 @@ public final class MediaStore {
public interface MediaColumns extends BaseColumns {
/**
- * The data stream for the file
- * <P>Type: DATA STREAM</P>
+ * Path to the file on disk.
+ * <p>
+ * Note that apps may not have filesystem permissions to directly access
+ * this path. Instead of trying to open this path directly, apps should
+ * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+ * access.
+ * <p>
+ * Type: TEXT
*/
public static final String DATA = "_data";
@@ -1149,8 +1155,15 @@ public final class MediaStore {
public static final String DEFAULT_SORT_ORDER = "image_id ASC";
/**
- * The data stream for the thumbnail
- * <P>Type: DATA STREAM</P>
+ * Path to the thumbnail file on disk.
+ * <p>
+ * Note that apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path directly,
+ * apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+ * access.
+ * <p>
+ * Type: TEXT
*/
public static final String DATA = "_data";
@@ -1596,8 +1609,15 @@ public final class MediaStore {
public static final String NAME = "name";
/**
- * The data stream for the playlist file
- * <P>Type: DATA STREAM</P>
+ * Path to the playlist file on disk.
+ * <p>
+ * Note that apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path directly,
+ * apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+ * access.
+ * <p>
+ * Type: TEXT
*/
public static final String DATA = "_data";
@@ -2192,8 +2212,15 @@ public final class MediaStore {
public static final String DEFAULT_SORT_ORDER = "video_id ASC";
/**
- * The data stream for the thumbnail
- * <P>Type: DATA STREAM</P>
+ * Path to the thumbnail file on disk.
+ * <p>
+ * Note that apps may not have filesystem permissions to directly
+ * access this path. Instead of trying to open this path directly,
+ * apps should use
+ * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+ * access.
+ * <p>
+ * Type: TEXT
*/
public static final String DATA = "_data";
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index c05ea2e1a1e5..704f0ce7624b 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -38,13 +38,14 @@ import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* The main programming interface for the DRM framework. An application must instantiate this class
* to access DRM agents through the DRM framework.
*
*/
-public class DrmManagerClient {
+public class DrmManagerClient implements AutoCloseable {
/**
* Indicates that a request was successful or that no error occurred.
*/
@@ -61,6 +62,7 @@ public class DrmManagerClient {
HandlerThread mEventThread;
private static final String TAG = "DrmManagerClient";
+ private final AtomicBoolean mClosed = new AtomicBoolean();
private final CloseGuard mCloseGuard = CloseGuard.get();
static {
@@ -117,7 +119,6 @@ public class DrmManagerClient {
private int mUniqueId;
private long mNativeContext;
- private volatile boolean mReleased;
private Context mContext;
private InfoHandler mInfoHandler;
private EventHandler mEventHandler;
@@ -261,41 +262,47 @@ public class DrmManagerClient {
@Override
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
- release();
+ mCloseGuard.warnIfOpen();
+ close();
} finally {
super.finalize();
}
}
/**
- * Releases resources associated with the current session of DrmManagerClient.
- *
- * It is considered good practice to call this method when the {@link DrmManagerClient} object
- * is no longer needed in your application. After release() is called,
- * {@link DrmManagerClient} is no longer usable since it has lost all of its required resource.
+ * Releases resources associated with the current session of
+ * DrmManagerClient. It is considered good practice to call this method when
+ * the {@link DrmManagerClient} object is no longer needed in your
+ * application. After this method is called, {@link DrmManagerClient} is no
+ * longer usable since it has lost all of its required resource.
*/
- public void release() {
- if (mReleased) return;
- mReleased = true;
-
- if (mEventHandler != null) {
- mEventThread.quit();
- mEventThread = null;
- }
- if (mInfoHandler != null) {
- mInfoThread.quit();
- mInfoThread = null;
- }
- mEventHandler = null;
- mInfoHandler = null;
- mOnEventListener = null;
- mOnInfoListener = null;
- mOnErrorListener = null;
- _release(mUniqueId);
+ @Override
+ public void close() {
mCloseGuard.close();
+ if (mClosed.compareAndSet(false, true)) {
+ if (mEventHandler != null) {
+ mEventThread.quit();
+ mEventThread = null;
+ }
+ if (mInfoHandler != null) {
+ mInfoThread.quit();
+ mInfoThread = null;
+ }
+ mEventHandler = null;
+ mInfoHandler = null;
+ mOnEventListener = null;
+ mOnInfoListener = null;
+ mOnErrorListener = null;
+ _release(mUniqueId);
+ }
+ }
+
+ /**
+ * @deprecated replaced by {@link #close()}.
+ */
+ @Deprecated
+ public void release() {
+ close();
}
/**
diff --git a/media/java/android/media/MediaInserter.java b/media/java/android/media/MediaInserter.java
index 41b369dc38ae..dd069218463a 100644
--- a/media/java/android/media/MediaInserter.java
+++ b/media/java/android/media/MediaInserter.java
@@ -16,8 +16,8 @@
package android.media;
+import android.content.ContentProviderClient;
import android.content.ContentValues;
-import android.content.IContentProvider;
import android.net.Uri;
import android.os.RemoteException;
@@ -37,13 +37,11 @@ public class MediaInserter {
private final HashMap<Uri, List<ContentValues>> mPriorityRowMap =
new HashMap<Uri, List<ContentValues>>();
- private final IContentProvider mProvider;
- private final String mPackageName;
+ private final ContentProviderClient mProvider;
private final int mBufferSizePerUri;
- public MediaInserter(IContentProvider provider, String packageName, int bufferSizePerUri) {
+ public MediaInserter(ContentProviderClient provider, int bufferSizePerUri) {
mProvider = provider;
- mPackageName = packageName;
mBufferSizePerUri = bufferSizePerUri;
}
@@ -90,7 +88,7 @@ public class MediaInserter {
if (!list.isEmpty()) {
ContentValues[] valuesArray = new ContentValues[list.size()];
valuesArray = list.toArray(valuesArray);
- mProvider.bulkInsert(mPackageName, tableUri, valuesArray);
+ mProvider.bulkInsert(tableUri, valuesArray);
list.clear();
}
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9ea672287d88..96c616be359d 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -16,14 +16,10 @@
package android.media;
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
+import android.content.ContentProviderClient;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.IContentProvider;
import android.database.Cursor;
import android.database.SQLException;
import android.drm.DrmManagerClient;
@@ -50,6 +46,12 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
+import dalvik.system.CloseGuard;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileDescriptor;
@@ -61,6 +63,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Internal service helper that no-one should use directly.
@@ -107,8 +110,7 @@ import java.util.Locale;
*
* {@hide}
*/
-public class MediaScanner
-{
+public class MediaScanner implements AutoCloseable {
static {
System.loadLibrary("media_jni");
native_init();
@@ -302,21 +304,23 @@ public class MediaScanner
};
private long mNativeContext;
- private Context mContext;
- private String mPackageName;
- private IContentProvider mMediaProvider;
- private Uri mAudioUri;
- private Uri mVideoUri;
- private Uri mImagesUri;
- private Uri mThumbsUri;
- private Uri mPlaylistsUri;
- private Uri mFilesUri;
- private Uri mFilesUriNoNotify;
- private boolean mProcessPlaylists, mProcessGenres;
+ private final Context mContext;
+ private final String mPackageName;
+ private final String mVolumeName;
+ private final ContentProviderClient mMediaProvider;
+ private final Uri mAudioUri;
+ private final Uri mVideoUri;
+ private final Uri mImagesUri;
+ private final Uri mThumbsUri;
+ private final Uri mPlaylistsUri;
+ private final Uri mFilesUri;
+ private final Uri mFilesUriNoNotify;
+ private final boolean mProcessPlaylists;
+ private final boolean mProcessGenres;
private int mMtpObjectHandle;
- private final String mExternalStoragePath;
- private final boolean mExternalIsEmulated;
+ private final AtomicBoolean mClosed = new AtomicBoolean();
+ private final CloseGuard mCloseGuard = CloseGuard.get();
/** whether to use bulk inserts or individual inserts for each item */
private static final boolean ENABLE_BULK_INSERTS = true;
@@ -345,10 +349,6 @@ public class MediaScanner
*/
private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
- // set to true if file path comparisons should be case insensitive.
- // this should be set when scanning files on a case insensitive file system.
- private boolean mCaseInsensitivePaths;
-
private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
private static class FileEntry {
@@ -378,26 +378,59 @@ public class MediaScanner
int bestmatchlevel;
}
- private ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<PlaylistEntry>();
+ private final ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<>();
+ private final ArrayList<FileEntry> mPlayLists = new ArrayList<>();
private MediaInserter mMediaInserter;
- private ArrayList<FileEntry> mPlayLists;
-
private DrmManagerClient mDrmManagerClient = null;
- public MediaScanner(Context c) {
+ public MediaScanner(Context c, String volumeName) {
native_setup();
mContext = c;
mPackageName = c.getPackageName();
+ mVolumeName = volumeName;
+
mBitmapOptions.inSampleSize = 1;
mBitmapOptions.inJustDecodeBounds = true;
setDefaultRingtoneFileNames();
- mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
- mExternalIsEmulated = Environment.isExternalStorageEmulated();
- //mClient.testGenreNameConverter();
+ mMediaProvider = mContext.getContentResolver()
+ .acquireContentProviderClient(MediaStore.AUTHORITY);
+
+ mAudioUri = Audio.Media.getContentUri(volumeName);
+ mVideoUri = Video.Media.getContentUri(volumeName);
+ mImagesUri = Images.Media.getContentUri(volumeName);
+ mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
+ mFilesUri = Files.getContentUri(volumeName);
+ mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
+
+ if (!volumeName.equals("internal")) {
+ // we only support playlists on external media
+ mProcessPlaylists = true;
+ mProcessGenres = true;
+ mPlaylistsUri = Playlists.getContentUri(volumeName);
+ } else {
+ mProcessPlaylists = false;
+ mProcessGenres = false;
+ mPlaylistsUri = null;
+ }
+
+ final Locale locale = mContext.getResources().getConfiguration().locale;
+ if (locale != null) {
+ String language = locale.getLanguage();
+ String country = locale.getCountry();
+ if (language != null) {
+ if (country != null) {
+ setLocale(language + "_" + country);
+ } else {
+ setLocale(language);
+ }
+ }
+ }
+
+ mCloseGuard.open("close");
}
private void setDefaultRingtoneFileNames() {
@@ -956,7 +989,7 @@ public class MediaScanner
if (inserter != null) {
inserter.flushAll();
}
- result = mMediaProvider.insert(mPackageName, tableUri, values);
+ result = mMediaProvider.insert(tableUri, values);
} else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
inserter.insertwithPriority(tableUri, values);
} else {
@@ -988,7 +1021,7 @@ public class MediaScanner
}
values.put(FileColumns.MEDIA_TYPE, mediaType);
}
- mMediaProvider.update(mPackageName, result, values, null, null);
+ mMediaProvider.update(result, values, null, null);
}
if(needToSetSettings) {
@@ -1055,11 +1088,7 @@ public class MediaScanner
String where = null;
String[] selectionArgs = null;
- if (mPlayLists == null) {
- mPlayLists = new ArrayList<FileEntry>();
- } else {
- mPlayLists.clear();
- }
+ mPlayLists.clear();
if (filePath != null) {
// query for only one file
@@ -1077,8 +1106,7 @@ public class MediaScanner
// filesystem is mounted and unmounted while the scanner is running).
Uri.Builder builder = mFilesUri.buildUpon();
builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
- MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, mPackageName,
- builder.build());
+ MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
// Build the list of files from the content provider
try {
@@ -1097,7 +1125,7 @@ public class MediaScanner
c.close();
c = null;
}
- c = mMediaProvider.query(mPackageName, limitUri, FILES_PRESCAN_PROJECTION,
+ c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
if (c == null) {
break;
@@ -1138,8 +1166,7 @@ public class MediaScanner
if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
deleter.flush();
String parent = new File(path).getParent();
- mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL,
- parent, null);
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
}
}
}
@@ -1157,7 +1184,7 @@ public class MediaScanner
// compute original size of images
mOriginalCount = 0;
- c = mMediaProvider.query(mPackageName, mImagesUri, ID_PROJECTION, null, null, null, null);
+ c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
if (c != null) {
mOriginalCount = c.getCount();
c.close();
@@ -1189,7 +1216,6 @@ public class MediaScanner
try {
c = mMediaProvider.query(
- mPackageName,
mThumbsUri,
new String [] { "_data" },
null,
@@ -1225,13 +1251,11 @@ public class MediaScanner
static class MediaBulkDeleter {
StringBuilder whereClause = new StringBuilder();
ArrayList<String> whereArgs = new ArrayList<String>(100);
- final IContentProvider mProvider;
- final String mPackageName;
+ final ContentProviderClient mProvider;
final Uri mBaseUri;
- public MediaBulkDeleter(IContentProvider provider, String packageName, Uri baseUri) {
+ public MediaBulkDeleter(ContentProviderClient provider, Uri baseUri) {
mProvider = provider;
- mPackageName = packageName;
mBaseUri = baseUri;
}
@@ -1250,7 +1274,7 @@ public class MediaScanner
if (size > 0) {
String [] foo = new String [size];
foo = whereArgs.toArray(foo);
- int numrows = mProvider.delete(mPackageName, mBaseUri,
+ int numrows = mProvider.delete(mBaseUri,
MediaStore.MediaColumns._ID + " IN (" +
whereClause.toString() + ")", foo);
//Log.i("@@@@@@@@@", "rows deleted: " + numrows);
@@ -1271,48 +1295,26 @@ public class MediaScanner
pruneDeadThumbnailFiles();
// allow GC to clean up
- mPlayLists = null;
- mMediaProvider = null;
+ mPlayLists.clear();
}
private void releaseResources() {
// release the DrmManagerClient resources
if (mDrmManagerClient != null) {
- mDrmManagerClient.release();
+ mDrmManagerClient.close();
mDrmManagerClient = null;
}
}
- private void initialize(String volumeName) {
- mMediaProvider = mContext.getContentResolver().acquireProvider("media");
-
- mAudioUri = Audio.Media.getContentUri(volumeName);
- mVideoUri = Video.Media.getContentUri(volumeName);
- mImagesUri = Images.Media.getContentUri(volumeName);
- mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
- mFilesUri = Files.getContentUri(volumeName);
- mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
-
- if (!volumeName.equals("internal")) {
- // we only support playlists on external media
- mProcessPlaylists = true;
- mProcessGenres = true;
- mPlaylistsUri = Playlists.getContentUri(volumeName);
-
- mCaseInsensitivePaths = true;
- }
- }
-
- public void scanDirectories(String[] directories, String volumeName) {
+ public void scanDirectories(String[] directories) {
try {
long start = System.currentTimeMillis();
- initialize(volumeName);
prescan(null, true);
long prescan = System.currentTimeMillis();
if (ENABLE_BULK_INSERTS) {
// create MediaInserter for bulk inserts
- mMediaInserter = new MediaInserter(mMediaProvider, mPackageName, 500);
+ mMediaInserter = new MediaInserter(mMediaProvider, 500);
}
for (int i = 0; i < directories.length; i++) {
@@ -1349,9 +1351,8 @@ public class MediaScanner
}
// this function is used to scan a single file
- public Uri scanSingleFile(String path, String volumeName, String mimeType) {
+ public Uri scanSingleFile(String path, String mimeType) {
try {
- initialize(volumeName);
prescan(path, true);
File file = new File(path);
@@ -1464,8 +1465,7 @@ public class MediaScanner
return isNoMediaFile(path);
}
- public void scanMtpFile(String path, String volumeName, int objectHandle, int format) {
- initialize(volumeName);
+ public void scanMtpFile(String path, int objectHandle, int format) {
MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
File file = new File(path);
@@ -1481,7 +1481,7 @@ public class MediaScanner
values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
try {
String[] whereArgs = new String[] { Integer.toString(objectHandle) };
- mMediaProvider.update(mPackageName, Files.getMtpObjectsUri(volumeName), values,
+ mMediaProvider.update(Files.getMtpObjectsUri(mVolumeName), values,
"_id=?", whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in scanMtpFile", e);
@@ -1498,7 +1498,7 @@ public class MediaScanner
FileEntry entry = makeEntryFor(path);
if (entry != null) {
- fileList = mMediaProvider.query(mPackageName, mFilesUri,
+ fileList = mMediaProvider.query(mFilesUri,
FILES_PRESCAN_PROJECTION, null, null, null, null);
processPlayList(entry, fileList);
}
@@ -1529,7 +1529,7 @@ public class MediaScanner
try {
where = Files.FileColumns.DATA + "=?";
selectionArgs = new String[] { path };
- c = mMediaProvider.query(mPackageName, mFilesUriNoNotify, FILES_PRESCAN_PROJECTION,
+ c = mMediaProvider.query(mFilesUriNoNotify, FILES_PRESCAN_PROJECTION,
where, selectionArgs, null, null);
if (c.moveToFirst()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
@@ -1641,7 +1641,7 @@ public class MediaScanner
values.clear();
values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
- mMediaProvider.insert(mPackageName, playlistUri, values);
+ mMediaProvider.insert(playlistUri, values);
index++;
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
@@ -1806,16 +1806,16 @@ public class MediaScanner
if (rowId == 0) {
values.put(MediaStore.Audio.Playlists.DATA, path);
- uri = mMediaProvider.insert(mPackageName, mPlaylistsUri, values);
+ uri = mMediaProvider.insert(mPlaylistsUri, values);
rowId = ContentUris.parseId(uri);
membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
} else {
uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
- mMediaProvider.update(mPackageName, uri, values, null, null);
+ mMediaProvider.update(uri, values, null, null);
// delete members of existing playlist
membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
- mMediaProvider.delete(mPackageName, membersUri, null, null);
+ mMediaProvider.delete(membersUri, null, null);
}
String playListDirectory = path.substring(0, lastSlash + 1);
@@ -1837,7 +1837,7 @@ public class MediaScanner
try {
// use the files uri and projection because we need the format column,
// but restrict the query to just audio files
- fileList = mMediaProvider.query(mPackageName, mFilesUri, FILES_PRESCAN_PROJECTION,
+ fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
"media_type=2", null, null, null);
while (iterator.hasNext()) {
FileEntry entry = iterator.next();
@@ -1856,7 +1856,7 @@ public class MediaScanner
private native void processDirectory(String path, MediaScannerClient client);
private native void processFile(String path, String mimeType, MediaScannerClient client);
- public native void setLocale(String locale);
+ private native void setLocale(String locale);
public native byte[] extractAlbumArt(FileDescriptor fd);
@@ -1864,19 +1864,22 @@ public class MediaScanner
private native final void native_setup();
private native final void native_finalize();
- /**
- * Releases resources associated with this MediaScanner object.
- * It is considered good practice to call this method when
- * one is done using the MediaScanner object. After this method
- * is called, the MediaScanner object can no longer be used.
- */
- public void release() {
- native_finalize();
+ @Override
+ public void close() {
+ mCloseGuard.close();
+ if (mClosed.compareAndSet(false, true)) {
+ mMediaProvider.close();
+ native_finalize();
+ }
}
@Override
- protected void finalize() {
- mContext.getContentResolver().releaseProvider(mMediaProvider);
- native_finalize();
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
+ }
}
}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 3541fba7f3b1..bc96e2ea8989 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -17,9 +17,9 @@
package android.mtp;
import android.content.BroadcastReceiver;
-import android.content.Context;
+import android.content.ContentProviderClient;
import android.content.ContentValues;
-import android.content.IContentProvider;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
@@ -37,23 +37,30 @@ import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
+import dalvik.system.CloseGuard;
+
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* {@hide}
*/
-public class MtpDatabase {
-
+public class MtpDatabase implements AutoCloseable {
private static final String TAG = "MtpDatabase";
private final Context mContext;
private final String mPackageName;
- private final IContentProvider mMediaProvider;
+ private final ContentProviderClient mMediaProvider;
private final String mVolumeName;
private final Uri mObjectsUri;
+ private final MediaScanner mMediaScanner;
+
+ private final AtomicBoolean mClosed = new AtomicBoolean();
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
// path to primary storage
private final String mMediaStoragePath;
// if not null, restrict all queries to these subdirectories
@@ -120,7 +127,6 @@ public class MtpDatabase {
private static final String STORAGE_FORMAT_PARENT_WHERE = STORAGE_FORMAT_WHERE + " AND "
+ Files.FileColumns.PARENT + "=?";
- private final MediaScanner mMediaScanner;
private MtpServer mServer;
// read from native code
@@ -156,11 +162,12 @@ public class MtpDatabase {
mContext = context;
mPackageName = context.getPackageName();
- mMediaProvider = context.getContentResolver().acquireProvider("media");
+ mMediaProvider = context.getContentResolver()
+ .acquireContentProviderClient(MediaStore.AUTHORITY);
mVolumeName = volumeName;
mMediaStoragePath = storagePath;
mObjectsUri = Files.getMtpObjectsUri(volumeName);
- mMediaScanner = new MediaScanner(context);
+ mMediaScanner = new MediaScanner(context, mVolumeName);
mSubDirectories = subDirectories;
if (subDirectories != null) {
@@ -187,20 +194,9 @@ public class MtpDatabase {
}
}
- // Set locale to MediaScanner.
- Locale locale = context.getResources().getConfiguration().locale;
- if (locale != null) {
- String language = locale.getLanguage();
- String country = locale.getCountry();
- if (language != null) {
- if (country != null) {
- mMediaScanner.setLocale(language + "_" + country);
- } else {
- mMediaScanner.setLocale(language);
- }
- }
- }
initDeviceProperties(context);
+
+ mCloseGuard.open("close");
}
public void setServer(MtpServer server) {
@@ -221,9 +217,20 @@ public class MtpDatabase {
}
@Override
+ public void close() {
+ mCloseGuard.close();
+ if (mClosed.compareAndSet(false, true)) {
+ mMediaScanner.close();
+ mMediaProvider.close();
+ native_finalize();
+ }
+ }
+
+ @Override
protected void finalize() throws Throwable {
try {
- native_finalize();
+ mCloseGuard.warnIfOpen();
+ close();
} finally {
super.finalize();
}
@@ -334,7 +341,7 @@ public class MtpDatabase {
if (path != null) {
Cursor c = null;
try {
- c = mMediaProvider.query(mPackageName, mObjectsUri, ID_PROJECTION, PATH_WHERE,
+ c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
new String[] { path }, null, null);
if (c != null && c.getCount() > 0) {
Log.w(TAG, "file already exists in beginSendObject: " + path);
@@ -359,7 +366,7 @@ public class MtpDatabase {
values.put(Files.FileColumns.DATE_MODIFIED, modified);
try {
- Uri uri = mMediaProvider.insert(mPackageName, mObjectsUri, values);
+ Uri uri = mMediaProvider.insert(mObjectsUri, values);
if (uri != null) {
return Integer.parseInt(uri.getPathSegments().get(2));
} else {
@@ -394,13 +401,13 @@ public class MtpDatabase {
values.put(Files.FileColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
try {
- Uri uri = mMediaProvider.insert(mPackageName,
+ Uri uri = mMediaProvider.insert(
Audio.Playlists.EXTERNAL_CONTENT_URI, values);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in endSendObject", e);
}
} else {
- mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
+ mMediaScanner.scanMtpFile(path, handle, format);
}
} else {
deleteFile(handle);
@@ -503,7 +510,7 @@ public class MtpDatabase {
}
}
- return mMediaProvider.query(mPackageName, mObjectsUri, ID_PROJECTION, where,
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where,
whereArgs, null, null);
}
@@ -721,7 +728,7 @@ public class MtpDatabase {
propertyGroup = mPropertyGroupsByFormat.get(format);
if (propertyGroup == null) {
int[] propertyList = getSupportedObjectProperties(format);
- propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mPackageName,
+ propertyGroup = new MtpPropertyGroup(this, mMediaProvider,
mVolumeName, propertyList);
mPropertyGroupsByFormat.put(new Integer(format), propertyGroup);
}
@@ -729,7 +736,7 @@ public class MtpDatabase {
propertyGroup = mPropertyGroupsByProperty.get(property);
if (propertyGroup == null) {
int[] propertyList = new int[] { (int)property };
- propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mPackageName,
+ propertyGroup = new MtpPropertyGroup(this, mMediaProvider,
mVolumeName, propertyList);
mPropertyGroupsByProperty.put(new Integer((int)property), propertyGroup);
}
@@ -745,7 +752,7 @@ public class MtpDatabase {
String path = null;
String[] whereArgs = new String[] { Integer.toString(handle) };
try {
- c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_PROJECTION, ID_WHERE,
+ c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE,
whereArgs, null, null);
if (c != null && c.moveToNext()) {
path = c.getString(1);
@@ -788,7 +795,7 @@ public class MtpDatabase {
try {
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
- updated = mMediaProvider.update(mPackageName, mObjectsUri, values, ID_WHERE, whereArgs);
+ updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
@@ -805,7 +812,7 @@ public class MtpDatabase {
if (oldFile.getName().startsWith(".") && !newPath.startsWith(".")) {
// directory was unhidden
try {
- mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, newPath, null);
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, newPath, null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + newPath);
}
@@ -815,7 +822,7 @@ public class MtpDatabase {
if (oldFile.getName().toLowerCase(Locale.US).equals(".nomedia")
&& !newPath.toLowerCase(Locale.US).equals(".nomedia")) {
try {
- mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + newPath);
}
@@ -886,7 +893,7 @@ public class MtpDatabase {
char[] outName, long[] outCreatedModified) {
Cursor c = null;
try {
- c = mMediaProvider.query(mPackageName, mObjectsUri, OBJECT_INFO_PROJECTION,
+ c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
outStorageFormatParent[0] = c.getInt(1);
@@ -933,7 +940,7 @@ public class MtpDatabase {
}
Cursor c = null;
try {
- c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_FORMAT_PROJECTION,
+ c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
String path = c.getString(1);
@@ -960,7 +967,7 @@ public class MtpDatabase {
private int getObjectFormat(int handle) {
Cursor c = null;
try {
- c = mMediaProvider.query(mPackageName, mObjectsUri, FORMAT_PROJECTION,
+ c = mMediaProvider.query(mObjectsUri, FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
return c.getInt(1);
@@ -984,7 +991,7 @@ public class MtpDatabase {
Cursor c = null;
try {
- c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_FORMAT_PROJECTION,
+ c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
// don't convert to media path here, since we will be matching
@@ -1007,7 +1014,7 @@ public class MtpDatabase {
if (format == MtpConstants.FORMAT_ASSOCIATION) {
// recursive case - delete all children first
Uri uri = Files.getMtpObjectsUri(mVolumeName);
- int count = mMediaProvider.delete(mPackageName, uri,
+ int count = mMediaProvider.delete(uri,
// the 'like' makes it use the index, the 'lower()' makes it correct
// when the path contains sqlite wildcard characters
"_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
@@ -1015,12 +1022,12 @@ public class MtpDatabase {
}
Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
- if (mMediaProvider.delete(mPackageName, uri, null, null) > 0) {
+ if (mMediaProvider.delete(uri, null, null) > 0) {
if (format != MtpConstants.FORMAT_ASSOCIATION
&& path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
try {
String parentPath = path.substring(0, path.lastIndexOf("/"));
- mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, parentPath, null);
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, parentPath, null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + path);
}
@@ -1043,7 +1050,7 @@ public class MtpDatabase {
Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
Cursor c = null;
try {
- c = mMediaProvider.query(mPackageName, uri, ID_PROJECTION, null, null, null, null);
+ c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null, null);
if (c == null) {
return null;
}
@@ -1077,7 +1084,7 @@ public class MtpDatabase {
valuesList[i] = values;
}
try {
- if (mMediaProvider.bulkInsert(mPackageName, uri, valuesList) > 0) {
+ if (mMediaProvider.bulkInsert(uri, valuesList) > 0) {
return MtpConstants.RESPONSE_OK;
}
} catch (RemoteException e) {
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index c80adfa80fc9..dea300838385 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -16,7 +16,7 @@
package android.mtp;
-import android.content.IContentProvider;
+import android.content.ContentProviderClient;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
@@ -48,8 +48,7 @@ class MtpPropertyGroup {
}
private final MtpDatabase mDatabase;
- private final IContentProvider mProvider;
- private final String mPackageName;
+ private final ContentProviderClient mProvider;
private final String mVolumeName;
private final Uri mUri;
@@ -65,13 +64,12 @@ class MtpPropertyGroup {
private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " + FORMAT_WHERE;
// constructs a property group for a list of properties
- public MtpPropertyGroup(MtpDatabase database, IContentProvider provider, String packageName,
- String volume, int[] properties) {
+ public MtpPropertyGroup(MtpDatabase database, ContentProviderClient provider, String volumeName,
+ int[] properties) {
mDatabase = database;
mProvider = provider;
- mPackageName = packageName;
- mVolumeName = volume;
- mUri = Files.getMtpObjectsUri(volume);
+ mVolumeName = volumeName;
+ mUri = Files.getMtpObjectsUri(volumeName);
int count = properties.length;
ArrayList<String> columns = new ArrayList<String>(count);
@@ -201,7 +199,7 @@ class MtpPropertyGroup {
Cursor c = null;
try {
// for now we are only reading properties from the "objects" table
- c = mProvider.query(mPackageName, mUri,
+ c = mProvider.query(mUri,
new String [] { Files.FileColumns._ID, column },
ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
@@ -221,7 +219,7 @@ class MtpPropertyGroup {
private String queryAudio(int id, String column) {
Cursor c = null;
try {
- c = mProvider.query(mPackageName, Audio.Media.getContentUri(mVolumeName),
+ c = mProvider.query(Audio.Media.getContentUri(mVolumeName),
new String [] { Files.FileColumns._ID, column },
ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
@@ -242,7 +240,7 @@ class MtpPropertyGroup {
Cursor c = null;
try {
Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
- c = mProvider.query(mPackageName, uri,
+ c = mProvider.query(uri,
new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
null, null, null, null);
if (c != null && c.moveToNext()) {
@@ -264,7 +262,7 @@ class MtpPropertyGroup {
Cursor c = null;
try {
// for now we are only reading properties from the "objects" table
- c = mProvider.query(mPackageName, mUri,
+ c = mProvider.query(mUri,
new String [] { Files.FileColumns._ID, column },
ID_WHERE, new String[] { Integer.toString(id) }, null, null);
if (c != null && c.moveToNext()) {
@@ -335,7 +333,7 @@ class MtpPropertyGroup {
try {
// don't query if not necessary
if (depth > 0 || handle == 0xFFFFFFFF || mColumns.length > 1) {
- c = mProvider.query(mPackageName, mUri, mColumns, where, whereArgs, null, null);
+ c = mProvider.query(mUri, mColumns, where, whereArgs, null, null);
if (c == null) {
return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index 05df014ad611..06efa906af04 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -16,6 +16,7 @@
package com.android.mediaframeworktest.unit;
+import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.IContentProvider;
import android.media.MediaInserter;
@@ -81,8 +82,9 @@ public class MediaInserterTest extends InstrumentationTestCase {
protected void setUp() throws Exception {
super.setUp();
mMockProvider = EasyMock.createMock(IContentProvider.class);
- mMediaInserter = new MediaInserter(mMockProvider,
- mPackageName, TEST_BUFFER_SIZE);
+ final ContentProviderClient client = new ContentProviderClient(
+ getInstrumentation().getContext().getContentResolver(), mMockProvider, true);
+ mMediaInserter = new MediaInserter(client, TEST_BUFFER_SIZE);
mPackageName = getInstrumentation().getContext().getPackageName();
mFilesCounter = 0;
mAudioCounter = 0;