Loading core/java/android/content/ContentProvider.java +23 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.database.IContentObserver; import android.database.SQLException; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; Loading Loading @@ -217,6 +218,13 @@ public abstract class ContentProvider implements ComponentCallbacks { return ContentProvider.this.openAssetFile(uri, mode); } /** * @hide */ public Bundle call(String method, String request, Bundle args) { return ContentProvider.this.call(method, request, args); } private void enforceReadPermission(Uri uri) { final int uid = Binder.getCallingUid(); if (uid == mMyUid) { Loading Loading @@ -748,4 +756,18 @@ public abstract class ContentProvider implements ComponentCallbacks { } return results; } /** * @hide -- until interface has proven itself * * Call an provider-defined method. This can be used to implement * interfaces that are cheaper than using a Cursor. * * @param method Method name to call. Opaque to framework. * @param request Nullable String argument passed to method. * @param args Nullable Bundle argument passed to method. */ public Bundle call(String method, String request, Bundle args) { return null; } } core/java/android/content/ContentProviderNative.java +33 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.database.IBulkCursor; import android.database.IContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; Loading Loading @@ -222,6 +223,21 @@ abstract public class ContentProviderNative extends Binder implements IContentPr } return true; } case CALL_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); String method = data.readString(); String stringArg = data.readString(); Bundle args = data.readBundle(); Bundle responseBundle = call(method, stringArg, args); reply.writeNoException(); reply.writeBundle(responseBundle); return true; } } } catch (Exception e) { DatabaseUtils.writeExceptionToParcel(reply, e); Loading Loading @@ -485,6 +501,22 @@ final class ContentProviderProxy implements IContentProvider return fd; } private IBinder mRemote; public Bundle call(String method, String request, Bundle args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(method); data.writeString(request); data.writeBundle(args); mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); return reply.readBundle(); } private IBinder mRemote; } core/java/android/content/IContentProvider.java +14 −1 Original line number Diff line number Diff line Loading @@ -22,10 +22,11 @@ import android.database.CursorWindow; import android.database.IBulkCursor; import android.database.IContentObserver; import android.net.Uri; import android.os.RemoteException; import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import java.io.FileNotFoundException; import java.util.ArrayList; Loading Loading @@ -58,6 +59,17 @@ public interface IContentProvider extends IInterface { throws RemoteException, FileNotFoundException; public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException; /** * @hide -- until interface has proven itself * * Call an provider-defined method. This can be used to implement * interfaces that are cheaper than using a Cursor. * * @param method Method name to call. Opaque to framework. * @param request Nullable String argument passed to method. * @param args Nullable Bundle argument passed to method. */ public Bundle call(String method, String request, Bundle args) throws RemoteException; /* IPC constants */ static final String descriptor = "android.content.IContentProvider"; Loading @@ -71,4 +83,5 @@ public interface IContentProvider extends IInterface { static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13; static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14; static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19; static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20; } core/java/android/os/Bundle.java +39 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,45 @@ public final class Bundle implements Parcelable, Cloneable { mClassLoader = b.mClassLoader; } /** * Make a Bundle for a single key/value pair. * * @hide */ public static Bundle forPair(String key, String value) { // TODO: optimize this case. Bundle b = new Bundle(1); b.putString(key, value); return b; } /** * TODO: optimize this later (getting just the value part of a Bundle * with a single pair) once Bundle.forPair() above is implemented * with a special single-value Map implementation/serialization. * * Note: value in single-pair Bundle may be null. * * @hide */ public String getPairValue() { unparcel(); int size = mMap.size(); if (size > 1) { Log.w(LOG_TAG, "getPairValue() used on Bundle with multiple pairs."); } if (size == 0) { return null; } Object o = mMap.values().iterator().next(); try { return (String) o; } catch (ClassCastException e) { typeWarning("getPairValue()", o, "String", e); return null; } } /** * Changes the ClassLoader this Bundle uses when instantiating objects. * Loading core/java/android/provider/Settings.java +73 −15 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.content.ContentQueryMap; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; Loading Loading @@ -491,6 +492,16 @@ public final class Settings { // End of Intent actions for Settings /** * @hide - Private call() method on SettingsProvider to read from 'system' table. */ public static final String CALL_METHOD_GET_SYSTEM = "GET_system"; /** * @hide - Private call() method on SettingsProvider to read from 'secure' table. */ public static final String CALL_METHOD_GET_SECURE = "GET_secure"; /** * Activity Extra: Limit available options in launched activity based on the given authority. * <p> Loading Loading @@ -544,23 +555,36 @@ public final class Settings { } } // Thread-safe. private static class NameValueCache { private final String mVersionSystemProperty; private final Uri mUri; // Must synchronize(mValues) to access mValues and mValuesVersion. private static final String[] SELECT_VALUE = new String[] { Settings.NameValueTable.VALUE }; private static final String NAME_EQ_PLACEHOLDER = "name=?"; // Must synchronize on 'this' to access mValues and mValuesVersion. private final HashMap<String, String> mValues = new HashMap<String, String>(); private long mValuesVersion = 0; public NameValueCache(String versionSystemProperty, Uri uri) { // Initially null; set lazily and held forever. Synchronized on 'this'. private IContentProvider mContentProvider = null; // The method we'll call (or null, to not use) on the provider // for the fast path of retrieving settings. private final String mCallCommand; public NameValueCache(String versionSystemProperty, Uri uri, String callCommand) { mVersionSystemProperty = versionSystemProperty; mUri = uri; mCallCommand = callCommand; } public String getString(ContentResolver cr, String name) { long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0); synchronized (mValues) { synchronized (this) { if (mValuesVersion != newValuesVersion) { if (LOCAL_LOGV) { Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current " + Loading @@ -576,17 +600,47 @@ public final class Settings { } } IContentProvider cp = null; synchronized (this) { cp = mContentProvider; if (cp == null) { cp = mContentProvider = cr.acquireProvider(mUri.getAuthority()); } } // Try the fast path first, not using query(). If this // fails (alternate Settings provider that doesn't support // this interface?) then we fall back to the query/table // interface. if (mCallCommand != null) { try { Bundle b = cp.call(mCallCommand, name, null); if (b != null) { String value = b.getPairValue(); synchronized (this) { mValues.put(name, value); } return value; } // If the response Bundle is null, we fall through // to the query interface below. } catch (RemoteException e) { // Not supported by the remote side? Fall through // to query(). } } Cursor c = null; try { c = cr.query(mUri, new String[] { Settings.NameValueTable.VALUE }, Settings.NameValueTable.NAME + "=?", new String[]{name}, null); c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER, new String[]{name}, null); if (c == null) { Log.w(TAG, "Can't get key " + name + " from " + mUri); return null; } String value = c.moveToNext() ? c.getString(0) : null; synchronized (mValues) { synchronized (this) { mValues.put(name, value); } if (LOCAL_LOGV) { Loading @@ -594,7 +648,7 @@ public final class Settings { name + " = " + (value == null ? "(null)" : value)); } return value; } catch (SQLException e) { } catch (RemoteException e) { Log.w(TAG, "Can't get key " + name + " from " + mUri, e); return null; // Return null, but don't cache it. } finally { Loading @@ -611,7 +665,8 @@ public final class Settings { public static final class System extends NameValueTable { public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version"; private static volatile NameValueCache mNameValueCache = null; // Populated lazily, guarded by class object: private static NameValueCache sNameValueCache = null; private static final HashSet<String> MOVED_TO_SECURE; static { Loading Loading @@ -660,10 +715,11 @@ public final class Settings { + " to android.provider.Settings.Secure, returning read-only value."); return Secure.getString(resolver, name); } if (mNameValueCache == null) { mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI); if (sNameValueCache == null) { sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI, CALL_METHOD_GET_SYSTEM); } return mNameValueCache.getString(resolver, name); return sNameValueCache.getString(resolver, name); } /** Loading Loading @@ -1905,7 +1961,8 @@ public final class Settings { public static final class Secure extends NameValueTable { public static final String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version"; private static volatile NameValueCache mNameValueCache = null; // Populated lazily, guarded by class object: private static NameValueCache sNameValueCache = null; /** * Look up a name in the database. Loading @@ -1914,10 +1971,11 @@ public final class Settings { * @return the corresponding value, or null if not present */ public synchronized static String getString(ContentResolver resolver, String name) { if (mNameValueCache == null) { mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI); if (sNameValueCache == null) { sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI, CALL_METHOD_GET_SECURE); } return mNameValueCache.getString(resolver, name); return sNameValueCache.getString(resolver, name); } /** Loading Loading
core/java/android/content/ContentProvider.java +23 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.database.IContentObserver; import android.database.SQLException; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; Loading Loading @@ -217,6 +218,13 @@ public abstract class ContentProvider implements ComponentCallbacks { return ContentProvider.this.openAssetFile(uri, mode); } /** * @hide */ public Bundle call(String method, String request, Bundle args) { return ContentProvider.this.call(method, request, args); } private void enforceReadPermission(Uri uri) { final int uid = Binder.getCallingUid(); if (uid == mMyUid) { Loading Loading @@ -748,4 +756,18 @@ public abstract class ContentProvider implements ComponentCallbacks { } return results; } /** * @hide -- until interface has proven itself * * Call an provider-defined method. This can be used to implement * interfaces that are cheaper than using a Cursor. * * @param method Method name to call. Opaque to framework. * @param request Nullable String argument passed to method. * @param args Nullable Bundle argument passed to method. */ public Bundle call(String method, String request, Bundle args) { return null; } }
core/java/android/content/ContentProviderNative.java +33 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.database.IBulkCursor; import android.database.IContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; Loading Loading @@ -222,6 +223,21 @@ abstract public class ContentProviderNative extends Binder implements IContentPr } return true; } case CALL_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); String method = data.readString(); String stringArg = data.readString(); Bundle args = data.readBundle(); Bundle responseBundle = call(method, stringArg, args); reply.writeNoException(); reply.writeBundle(responseBundle); return true; } } } catch (Exception e) { DatabaseUtils.writeExceptionToParcel(reply, e); Loading Loading @@ -485,6 +501,22 @@ final class ContentProviderProxy implements IContentProvider return fd; } private IBinder mRemote; public Bundle call(String method, String request, Bundle args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(method); data.writeString(request); data.writeBundle(args); mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); return reply.readBundle(); } private IBinder mRemote; }
core/java/android/content/IContentProvider.java +14 −1 Original line number Diff line number Diff line Loading @@ -22,10 +22,11 @@ import android.database.CursorWindow; import android.database.IBulkCursor; import android.database.IContentObserver; import android.net.Uri; import android.os.RemoteException; import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import java.io.FileNotFoundException; import java.util.ArrayList; Loading Loading @@ -58,6 +59,17 @@ public interface IContentProvider extends IInterface { throws RemoteException, FileNotFoundException; public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException; /** * @hide -- until interface has proven itself * * Call an provider-defined method. This can be used to implement * interfaces that are cheaper than using a Cursor. * * @param method Method name to call. Opaque to framework. * @param request Nullable String argument passed to method. * @param args Nullable Bundle argument passed to method. */ public Bundle call(String method, String request, Bundle args) throws RemoteException; /* IPC constants */ static final String descriptor = "android.content.IContentProvider"; Loading @@ -71,4 +83,5 @@ public interface IContentProvider extends IInterface { static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13; static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14; static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19; static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20; }
core/java/android/os/Bundle.java +39 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,45 @@ public final class Bundle implements Parcelable, Cloneable { mClassLoader = b.mClassLoader; } /** * Make a Bundle for a single key/value pair. * * @hide */ public static Bundle forPair(String key, String value) { // TODO: optimize this case. Bundle b = new Bundle(1); b.putString(key, value); return b; } /** * TODO: optimize this later (getting just the value part of a Bundle * with a single pair) once Bundle.forPair() above is implemented * with a special single-value Map implementation/serialization. * * Note: value in single-pair Bundle may be null. * * @hide */ public String getPairValue() { unparcel(); int size = mMap.size(); if (size > 1) { Log.w(LOG_TAG, "getPairValue() used on Bundle with multiple pairs."); } if (size == 0) { return null; } Object o = mMap.values().iterator().next(); try { return (String) o; } catch (ClassCastException e) { typeWarning("getPairValue()", o, "String", e); return null; } } /** * Changes the ClassLoader this Bundle uses when instantiating objects. * Loading
core/java/android/provider/Settings.java +73 −15 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.content.ContentQueryMap; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; Loading Loading @@ -491,6 +492,16 @@ public final class Settings { // End of Intent actions for Settings /** * @hide - Private call() method on SettingsProvider to read from 'system' table. */ public static final String CALL_METHOD_GET_SYSTEM = "GET_system"; /** * @hide - Private call() method on SettingsProvider to read from 'secure' table. */ public static final String CALL_METHOD_GET_SECURE = "GET_secure"; /** * Activity Extra: Limit available options in launched activity based on the given authority. * <p> Loading Loading @@ -544,23 +555,36 @@ public final class Settings { } } // Thread-safe. private static class NameValueCache { private final String mVersionSystemProperty; private final Uri mUri; // Must synchronize(mValues) to access mValues and mValuesVersion. private static final String[] SELECT_VALUE = new String[] { Settings.NameValueTable.VALUE }; private static final String NAME_EQ_PLACEHOLDER = "name=?"; // Must synchronize on 'this' to access mValues and mValuesVersion. private final HashMap<String, String> mValues = new HashMap<String, String>(); private long mValuesVersion = 0; public NameValueCache(String versionSystemProperty, Uri uri) { // Initially null; set lazily and held forever. Synchronized on 'this'. private IContentProvider mContentProvider = null; // The method we'll call (or null, to not use) on the provider // for the fast path of retrieving settings. private final String mCallCommand; public NameValueCache(String versionSystemProperty, Uri uri, String callCommand) { mVersionSystemProperty = versionSystemProperty; mUri = uri; mCallCommand = callCommand; } public String getString(ContentResolver cr, String name) { long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0); synchronized (mValues) { synchronized (this) { if (mValuesVersion != newValuesVersion) { if (LOCAL_LOGV) { Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current " + Loading @@ -576,17 +600,47 @@ public final class Settings { } } IContentProvider cp = null; synchronized (this) { cp = mContentProvider; if (cp == null) { cp = mContentProvider = cr.acquireProvider(mUri.getAuthority()); } } // Try the fast path first, not using query(). If this // fails (alternate Settings provider that doesn't support // this interface?) then we fall back to the query/table // interface. if (mCallCommand != null) { try { Bundle b = cp.call(mCallCommand, name, null); if (b != null) { String value = b.getPairValue(); synchronized (this) { mValues.put(name, value); } return value; } // If the response Bundle is null, we fall through // to the query interface below. } catch (RemoteException e) { // Not supported by the remote side? Fall through // to query(). } } Cursor c = null; try { c = cr.query(mUri, new String[] { Settings.NameValueTable.VALUE }, Settings.NameValueTable.NAME + "=?", new String[]{name}, null); c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER, new String[]{name}, null); if (c == null) { Log.w(TAG, "Can't get key " + name + " from " + mUri); return null; } String value = c.moveToNext() ? c.getString(0) : null; synchronized (mValues) { synchronized (this) { mValues.put(name, value); } if (LOCAL_LOGV) { Loading @@ -594,7 +648,7 @@ public final class Settings { name + " = " + (value == null ? "(null)" : value)); } return value; } catch (SQLException e) { } catch (RemoteException e) { Log.w(TAG, "Can't get key " + name + " from " + mUri, e); return null; // Return null, but don't cache it. } finally { Loading @@ -611,7 +665,8 @@ public final class Settings { public static final class System extends NameValueTable { public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version"; private static volatile NameValueCache mNameValueCache = null; // Populated lazily, guarded by class object: private static NameValueCache sNameValueCache = null; private static final HashSet<String> MOVED_TO_SECURE; static { Loading Loading @@ -660,10 +715,11 @@ public final class Settings { + " to android.provider.Settings.Secure, returning read-only value."); return Secure.getString(resolver, name); } if (mNameValueCache == null) { mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI); if (sNameValueCache == null) { sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI, CALL_METHOD_GET_SYSTEM); } return mNameValueCache.getString(resolver, name); return sNameValueCache.getString(resolver, name); } /** Loading Loading @@ -1905,7 +1961,8 @@ public final class Settings { public static final class Secure extends NameValueTable { public static final String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version"; private static volatile NameValueCache mNameValueCache = null; // Populated lazily, guarded by class object: private static NameValueCache sNameValueCache = null; /** * Look up a name in the database. Loading @@ -1914,10 +1971,11 @@ public final class Settings { * @return the corresponding value, or null if not present */ public synchronized static String getString(ContentResolver resolver, String name) { if (mNameValueCache == null) { mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI); if (sNameValueCache == null) { sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI, CALL_METHOD_GET_SECURE); } return mNameValueCache.getString(resolver, name); return sNameValueCache.getString(resolver, name); } /** Loading