decouple SyncAdapter from ContentProvider
This commit is contained in:
465
api/current.xml
465
api/current.xml
@@ -2528,6 +2528,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="contentAuthority"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="16843373"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="cropToPadding"
|
||||
type="int"
|
||||
transient="false"
|
||||
@@ -12848,123 +12859,6 @@
|
||||
</parameter>
|
||||
</method>
|
||||
</class>
|
||||
<class name="AccountAuthenticatorCache"
|
||||
extends="java.lang.Object"
|
||||
abstract="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<constructor name="AccountAuthenticatorCache"
|
||||
type="android.accounts.AccountAuthenticatorCache"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="context" type="android.content.Context">
|
||||
</parameter>
|
||||
</constructor>
|
||||
<method name="close"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="dump"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="protected"
|
||||
>
|
||||
<parameter name="fd" type="java.io.FileDescriptor">
|
||||
</parameter>
|
||||
<parameter name="fout" type="java.io.PrintWriter">
|
||||
</parameter>
|
||||
<parameter name="args" type="java.lang.String[]">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="getAllAuthenticators"
|
||||
return="java.util.Collection<android.accounts.AccountAuthenticatorCache.AuthenticatorInfo>"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="getAuthenticatorInfo"
|
||||
return="android.accounts.AccountAuthenticatorCache.AuthenticatorInfo"
|
||||
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="parseAuthenticatorInfo"
|
||||
return="android.accounts.AccountAuthenticatorCache.AuthenticatorInfo"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="service" type="android.content.pm.ResolveInfo">
|
||||
</parameter>
|
||||
<exception name="IOException" type="java.io.IOException">
|
||||
</exception>
|
||||
<exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException">
|
||||
</exception>
|
||||
</method>
|
||||
</class>
|
||||
<class name="AccountAuthenticatorCache.AuthenticatorInfo"
|
||||
extends="java.lang.Object"
|
||||
abstract="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<field name="mComponentName"
|
||||
type="android.content.ComponentName"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="mType"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
</class>
|
||||
<class name="AccountAuthenticatorResponse"
|
||||
extends="java.lang.Object"
|
||||
abstract="false"
|
||||
@@ -13784,93 +13678,6 @@
|
||||
</parameter>
|
||||
</method>
|
||||
</class>
|
||||
<class name="AuthenticatorBindHelper"
|
||||
extends="java.lang.Object"
|
||||
abstract="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<constructor name="AuthenticatorBindHelper"
|
||||
type="android.accounts.AuthenticatorBindHelper"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="context" type="android.content.Context">
|
||||
</parameter>
|
||||
<parameter name="authenticatorCache" type="android.accounts.AccountAuthenticatorCache">
|
||||
</parameter>
|
||||
<parameter name="handler" type="android.os.Handler">
|
||||
</parameter>
|
||||
<parameter name="messageWhatConnected" type="int">
|
||||
</parameter>
|
||||
<parameter name="messageWhatDisconnected" type="int">
|
||||
</parameter>
|
||||
</constructor>
|
||||
<method name="bind"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="authenticatorType" type="java.lang.String">
|
||||
</parameter>
|
||||
<parameter name="callback" type="android.accounts.AuthenticatorBindHelper.Callback">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="unbind"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="callbackToUnbind" type="android.accounts.AuthenticatorBindHelper.Callback">
|
||||
</parameter>
|
||||
</method>
|
||||
</class>
|
||||
<interface name="AuthenticatorBindHelper.Callback"
|
||||
abstract="true"
|
||||
static="true"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<method name="onConnected"
|
||||
return="void"
|
||||
abstract="true"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="service" type="android.os.IBinder">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onDisconnected"
|
||||
return="void"
|
||||
abstract="true"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
</interface>
|
||||
<class name="AuthenticatorException"
|
||||
extends="java.lang.Exception"
|
||||
abstract="false"
|
||||
@@ -25041,6 +24848,187 @@
|
||||
</parameter>
|
||||
</method>
|
||||
</class>
|
||||
<class name="ContentProviderClient"
|
||||
extends="java.lang.Object"
|
||||
abstract="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<method name="bulkInsert"
|
||||
return="int"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="url" type="android.net.Uri">
|
||||
</parameter>
|
||||
<parameter name="initialValues" type="android.content.ContentValues[]">
|
||||
</parameter>
|
||||
<exception name="RemoteException" type="android.os.RemoteException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="coerceToContentProvider"
|
||||
return="android.content.ContentProvider"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="delete"
|
||||
return="int"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="url" type="android.net.Uri">
|
||||
</parameter>
|
||||
<parameter name="selection" type="java.lang.String">
|
||||
</parameter>
|
||||
<parameter name="selectionArgs" type="java.lang.String[]">
|
||||
</parameter>
|
||||
<exception name="RemoteException" type="android.os.RemoteException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="getType"
|
||||
return="java.lang.String"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="url" type="android.net.Uri">
|
||||
</parameter>
|
||||
<exception name="RemoteException" type="android.os.RemoteException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="insert"
|
||||
return="android.net.Uri"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="url" type="android.net.Uri">
|
||||
</parameter>
|
||||
<parameter name="initialValues" type="android.content.ContentValues">
|
||||
</parameter>
|
||||
<exception name="RemoteException" type="android.os.RemoteException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="openAssetFile"
|
||||
return="android.content.res.AssetFileDescriptor"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="url" type="android.net.Uri">
|
||||
</parameter>
|
||||
<parameter name="mode" type="java.lang.String">
|
||||
</parameter>
|
||||
<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
|
||||
</exception>
|
||||
<exception name="RemoteException" type="android.os.RemoteException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="openFile"
|
||||
return="android.os.ParcelFileDescriptor"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="url" type="android.net.Uri">
|
||||
</parameter>
|
||||
<parameter name="mode" type="java.lang.String">
|
||||
</parameter>
|
||||
<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
|
||||
</exception>
|
||||
<exception name="RemoteException" type="android.os.RemoteException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="query"
|
||||
return="android.database.Cursor"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="url" type="android.net.Uri">
|
||||
</parameter>
|
||||
<parameter name="projection" type="java.lang.String[]">
|
||||
</parameter>
|
||||
<parameter name="selection" type="java.lang.String">
|
||||
</parameter>
|
||||
<parameter name="selectionArgs" type="java.lang.String[]">
|
||||
</parameter>
|
||||
<parameter name="sortOrder" type="java.lang.String">
|
||||
</parameter>
|
||||
<exception name="RemoteException" type="android.os.RemoteException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="release"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="update"
|
||||
return="int"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="url" type="android.net.Uri">
|
||||
</parameter>
|
||||
<parameter name="values" type="android.content.ContentValues">
|
||||
</parameter>
|
||||
<parameter name="selection" type="java.lang.String">
|
||||
</parameter>
|
||||
<parameter name="selectionArgs" type="java.lang.String[]">
|
||||
</parameter>
|
||||
<exception name="RemoteException" type="android.os.RemoteException">
|
||||
</exception>
|
||||
</method>
|
||||
</class>
|
||||
<class name="ContentQueryMap"
|
||||
extends="java.util.Observable"
|
||||
abstract="false"
|
||||
@@ -25143,6 +25131,32 @@
|
||||
<parameter name="context" type="android.content.Context">
|
||||
</parameter>
|
||||
</constructor>
|
||||
<method name="acquireContentProviderClient"
|
||||
return="android.content.ContentProviderClient"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="uri" type="android.net.Uri">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="acquireContentProviderClient"
|
||||
return="android.content.ContentProviderClient"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="name" type="java.lang.String">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="bulkInsert"
|
||||
return="int"
|
||||
abstract="false"
|
||||
@@ -33095,6 +33109,47 @@
|
||||
</parameter>
|
||||
</method>
|
||||
</interface>
|
||||
<class name="SyncAdapterType"
|
||||
extends="java.lang.Object"
|
||||
abstract="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<constructor name="SyncAdapterType"
|
||||
type="android.content.SyncAdapterType"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="authority" type="java.lang.String">
|
||||
</parameter>
|
||||
<parameter name="accountType" type="java.lang.String">
|
||||
</parameter>
|
||||
</constructor>
|
||||
<field name="accountType"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="authority"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
</class>
|
||||
<class name="UriMatcher"
|
||||
extends="java.lang.Object"
|
||||
abstract="false"
|
||||
@@ -36787,7 +36842,7 @@
|
||||
volatile="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
deprecated="deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.accounts;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.RegisteredServicesCache;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -46,172 +47,27 @@ import org.xmlpull.v1.XmlPullParser;
|
||||
* A cache of services that export the {@link IAccountAuthenticator} interface. This cache
|
||||
* is built by interrogating the {@link PackageManager} and is updated as packages are added,
|
||||
* removed and changed. The authenticators are referred to by their account type and
|
||||
* are made available via the {@link #getAuthenticatorInfo(String type)} method.
|
||||
* are made available via the {@link RegisteredServicesCache#getServiceInfo} method.
|
||||
* @hide
|
||||
*/
|
||||
public class AccountAuthenticatorCache {
|
||||
/* package private */ class AccountAuthenticatorCache extends RegisteredServicesCache<String> {
|
||||
private static final String TAG = "Account";
|
||||
|
||||
private static final String SERVICE_INTERFACE = "android.accounts.AccountAuthenticator";
|
||||
private static final String SERVICE_META_DATA = "android.accounts.AccountAuthenticator";
|
||||
|
||||
private volatile Map<String, AuthenticatorInfo> mAuthenticators;
|
||||
|
||||
private final Context mContext;
|
||||
private BroadcastReceiver mReceiver;
|
||||
private static final String ATTRIBUTES_NAME = "account-authenticator";
|
||||
|
||||
public AccountAuthenticatorCache(Context context) {
|
||||
mContext = context;
|
||||
mReceiver = new BroadcastReceiver() {
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
buildAuthenticatorList();
|
||||
}
|
||||
};
|
||||
super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME);
|
||||
}
|
||||
|
||||
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
||||
getAllAuthenticators();
|
||||
Map<String, AuthenticatorInfo> authenticators = mAuthenticators;
|
||||
fout.println("AccountAuthenticatorCache: " + authenticators.size() + " authenticators");
|
||||
for (AuthenticatorInfo info : authenticators.values()) {
|
||||
fout.println(" " + info);
|
||||
}
|
||||
}
|
||||
|
||||
private void monitorPackageChanges() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
mContext.registerReceiver(mReceiver, intentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Value type that describes an AccountAuthenticator. The information within can be used
|
||||
* to bind to its {@link IAccountAuthenticator} interface.
|
||||
*/
|
||||
public class AuthenticatorInfo {
|
||||
public final String mType;
|
||||
public final ComponentName mComponentName;
|
||||
|
||||
private AuthenticatorInfo(String type, ComponentName componentName) {
|
||||
mType = type;
|
||||
mComponentName = componentName;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "AuthenticatorInfo: " + mType + ", " + mComponentName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the registered authenticators.
|
||||
* @param type the account type of the authenticator
|
||||
* @return the AuthenticatorInfo that matches the account type or null if none is present
|
||||
*/
|
||||
public AuthenticatorInfo getAuthenticatorInfo(String type) {
|
||||
if (mAuthenticators == null) {
|
||||
monitorPackageChanges();
|
||||
buildAuthenticatorList();
|
||||
}
|
||||
return mAuthenticators.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a collection of {@link AuthenticatorInfo} objects for all
|
||||
* registered authenticators.
|
||||
*/
|
||||
public Collection<AuthenticatorInfo> getAllAuthenticators() {
|
||||
if (mAuthenticators == null) {
|
||||
monitorPackageChanges();
|
||||
buildAuthenticatorList();
|
||||
}
|
||||
return Collections.unmodifiableCollection(mAuthenticators.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the monitoring of package additions, removals and changes.
|
||||
*/
|
||||
public void close() {
|
||||
if (mReceiver != null) {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
mReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
if (mReceiver != null) {
|
||||
Log.e(TAG, "AccountAuthenticatorCache finalized without being closed");
|
||||
}
|
||||
close();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
private void buildAuthenticatorList() {
|
||||
Map<String, AuthenticatorInfo> authenticators = Maps.newHashMap();
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
|
||||
List<ResolveInfo> services =
|
||||
pm.queryIntentServices(new Intent(SERVICE_INTERFACE), PackageManager.GET_META_DATA);
|
||||
|
||||
for (ResolveInfo resolveInfo : services) {
|
||||
try {
|
||||
AuthenticatorInfo info = parseAuthenticatorInfo(resolveInfo);
|
||||
if (info != null) {
|
||||
authenticators.put(info.mType, info);
|
||||
} else {
|
||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString());
|
||||
}
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
mAuthenticators = authenticators;
|
||||
}
|
||||
|
||||
public AuthenticatorInfo parseAuthenticatorInfo(ResolveInfo service)
|
||||
throws XmlPullParserException, IOException {
|
||||
ServiceInfo si = service.serviceInfo;
|
||||
ComponentName componentName = new ComponentName(si.packageName, si.name);
|
||||
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
String authenticatorType = null;
|
||||
|
||||
XmlResourceParser parser = null;
|
||||
public String parseServiceAttributes(AttributeSet attrs) {
|
||||
TypedArray sa = mContext.getResources().obtainAttributes(attrs,
|
||||
com.android.internal.R.styleable.AccountAuthenticator);
|
||||
try {
|
||||
parser = si.loadXmlMetaData(pm, SERVICE_META_DATA);
|
||||
if (parser == null) {
|
||||
throw new XmlPullParserException("No " + SERVICE_META_DATA + " meta-data");
|
||||
}
|
||||
|
||||
AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
|
||||
int type;
|
||||
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& type != XmlPullParser.START_TAG) {
|
||||
}
|
||||
|
||||
String nodeName = parser.getName();
|
||||
if (!"account-authenticator".equals(nodeName)) {
|
||||
throw new XmlPullParserException(
|
||||
"Meta-data does not start with account-authenticator tag");
|
||||
}
|
||||
|
||||
TypedArray sa = mContext.getResources().obtainAttributes(attrs,
|
||||
com.android.internal.R.styleable.AccountAuthenticator);
|
||||
authenticatorType = sa.getString(
|
||||
com.android.internal.R.styleable.AccountAuthenticator_accountType);
|
||||
sa.recycle();
|
||||
return sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType);
|
||||
} finally {
|
||||
if (parser != null) parser.close();
|
||||
sa.recycle();
|
||||
}
|
||||
|
||||
if (authenticatorType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AuthenticatorInfo(authenticatorType, componentName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,13 +44,10 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import com.android.internal.telephony.TelephonyIntents;
|
||||
import com.android.internal.R;
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
/**
|
||||
* A system service that provides account, password, and authtoken management for all
|
||||
@@ -221,12 +218,13 @@ public class AccountManagerService extends IAccountManager.Stub {
|
||||
public String[] getAuthenticatorTypes() {
|
||||
long identityToken = clearCallingIdentity();
|
||||
try {
|
||||
Collection<AccountAuthenticatorCache.AuthenticatorInfo> authenticatorCollection =
|
||||
mAuthenticatorCache.getAllAuthenticators();
|
||||
Collection<AccountAuthenticatorCache.ServiceInfo<String>> authenticatorCollection =
|
||||
mAuthenticatorCache.getAllServices();
|
||||
String[] types = new String[authenticatorCollection.size()];
|
||||
int i = 0;
|
||||
for (AccountAuthenticatorCache.AuthenticatorInfo authenticator : authenticatorCollection) {
|
||||
types[i] = authenticator.mType;
|
||||
for (AccountAuthenticatorCache.ServiceInfo<String> authenticator
|
||||
: authenticatorCollection) {
|
||||
types[i] = authenticator.type;
|
||||
i++;
|
||||
}
|
||||
return types;
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.google.android.collect.Maps;
|
||||
* to the same authenticator, with each bind call guaranteed to return either
|
||||
* {@link Callback#onConnected} or {@link Callback#onDisconnected} if the bind() call
|
||||
* itself succeeds, even if the authenticator is already bound internally.
|
||||
* @hide
|
||||
*/
|
||||
public class AuthenticatorBindHelper {
|
||||
private static final String TAG = "Accounts";
|
||||
@@ -94,8 +95,8 @@ public class AuthenticatorBindHelper {
|
||||
|
||||
// otherwise find the component name for the authenticator and initiate a bind
|
||||
// if no authenticator or the bind fails then return false, otherwise return true
|
||||
AccountAuthenticatorCache.AuthenticatorInfo authenticatorInfo =
|
||||
mAuthenticatorCache.getAuthenticatorInfo(authenticatorType);
|
||||
AccountAuthenticatorCache.ServiceInfo authenticatorInfo =
|
||||
mAuthenticatorCache.getServiceInfo(authenticatorType);
|
||||
if (authenticatorInfo == null) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "there is no authenticator for " + authenticatorType
|
||||
@@ -108,13 +109,13 @@ public class AuthenticatorBindHelper {
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction("android.accounts.AccountAuthenticator");
|
||||
intent.setComponent(authenticatorInfo.mComponentName);
|
||||
intent.setComponent(authenticatorInfo.componentName);
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "performing bindService to " + authenticatorInfo.mComponentName);
|
||||
Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
|
||||
}
|
||||
if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "bindService to " + authenticatorInfo.mComponentName + " failed");
|
||||
Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -160,8 +160,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
|
||||
// work with.
|
||||
mDb = mOpenHelper.getWritableDatabase();
|
||||
onAccountsChanged(accounts);
|
||||
TempProviderSyncAdapter syncAdapter =
|
||||
(TempProviderSyncAdapter)getSyncAdapter();
|
||||
TempProviderSyncAdapter syncAdapter = getTempProviderSyncAdapter();
|
||||
if (syncAdapter != null) {
|
||||
syncAdapter.onAccountsChanged(accounts);
|
||||
}
|
||||
|
||||
@@ -170,12 +170,6 @@ public abstract class ContentProvider implements ComponentCallbacks {
|
||||
return ContentProvider.this.openAssetFile(uri, mode);
|
||||
}
|
||||
|
||||
public ISyncAdapter getSyncAdapter() {
|
||||
checkWritePermission(null);
|
||||
SyncAdapter sa = ContentProvider.this.getSyncAdapter();
|
||||
return sa != null ? sa.getISyncAdapter() : null;
|
||||
}
|
||||
|
||||
private void checkReadPermission(Uri uri) {
|
||||
final String rperm = getReadPermission();
|
||||
final int pid = Binder.getCallingPid();
|
||||
@@ -550,23 +544,6 @@ public abstract class ContentProvider implements ComponentCallbacks {
|
||||
return ParcelFileDescriptor.open(new File(path), modeBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sync adapter that is to be used by this content provider.
|
||||
* This is intended for use by the sync system. If null then this
|
||||
* content provider is considered not syncable.
|
||||
* This method can be called from multiple
|
||||
* threads, as described in
|
||||
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
|
||||
* Processes and Threads</a>.
|
||||
*
|
||||
* @return the SyncAdapter that is to be used by this ContentProvider, or null
|
||||
* if this ContentProvider is not syncable
|
||||
* @hide
|
||||
*/
|
||||
public SyncAdapter getSyncAdapter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this instance is a temporary content provider.
|
||||
* @return true if this instance is a temporary content provider
|
||||
|
||||
96
core/java/android/content/ContentProviderClient.java
Normal file
96
core/java/android/content/ContentProviderClient.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package android.content;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public class ContentProviderClient {
|
||||
private final IContentProvider mContentProvider;
|
||||
private final ContentResolver mContentResolver;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider) {
|
||||
mContentProvider = contentProvider;
|
||||
mContentResolver = contentResolver;
|
||||
}
|
||||
|
||||
/** {@see ContentProvider#query} */
|
||||
public Cursor query(Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) throws RemoteException {
|
||||
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
/** {@see ContentProvider#getType} */
|
||||
public String getType(Uri url) throws RemoteException {
|
||||
return mContentProvider.getType(url);
|
||||
}
|
||||
|
||||
/** {@see ContentProvider#insert} */
|
||||
public Uri insert(Uri url, ContentValues initialValues)
|
||||
throws RemoteException {
|
||||
return mContentProvider.insert(url, initialValues);
|
||||
}
|
||||
|
||||
/** {@see ContentProvider#bulkInsert} */
|
||||
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
|
||||
return mContentProvider.bulkInsert(url, initialValues);
|
||||
}
|
||||
|
||||
/** {@see ContentProvider#delete} */
|
||||
public int delete(Uri url, String selection, String[] selectionArgs)
|
||||
throws RemoteException {
|
||||
return mContentProvider.delete(url, selection, selectionArgs);
|
||||
}
|
||||
|
||||
/** {@see ContentProvider#update} */
|
||||
public int update(Uri url, ContentValues values, String selection,
|
||||
String[] selectionArgs) throws RemoteException {
|
||||
return mContentProvider.update(url, values, selection, selectionArgs);
|
||||
}
|
||||
|
||||
/** {@see ContentProvider#openFile} */
|
||||
public ParcelFileDescriptor openFile(Uri url, String mode)
|
||||
throws RemoteException, FileNotFoundException {
|
||||
return mContentProvider.openFile(url, mode);
|
||||
}
|
||||
|
||||
/** {@see ContentProvider#openAssetFile} */
|
||||
public AssetFileDescriptor openAssetFile(Uri url, String mode)
|
||||
throws RemoteException, FileNotFoundException {
|
||||
return mContentProvider.openAssetFile(url, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public boolean release() {
|
||||
return mContentResolver.releaseProvider(mContentProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the {@link ContentProvider} that is associated with this
|
||||
* client. If the {@link ContentProvider} is running in a different process then
|
||||
* null will be returned. This can be used if you know you are running in the same
|
||||
* process as a provider, and want to get direct access to its implementation details.
|
||||
*
|
||||
* @return If the associated {@link ContentProvider} is local, returns it.
|
||||
* Otherwise returns null.
|
||||
*/
|
||||
public ContentProvider getLocalContentProvider() {
|
||||
return ContentProvider.coerceToLocalContentProvider(mContentProvider);
|
||||
}
|
||||
}
|
||||
@@ -206,15 +206,6 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case GET_SYNC_ADAPTER_TRANSACTION:
|
||||
{
|
||||
data.enforceInterface(IContentProvider.descriptor);
|
||||
ISyncAdapter sa = getSyncAdapter();
|
||||
reply.writeNoException();
|
||||
reply.writeStrongBinder(sa != null ? sa.asBinder() : null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
DatabaseUtils.writeExceptionToParcel(reply, e);
|
||||
@@ -456,23 +447,6 @@ final class ContentProviderProxy implements IContentProvider
|
||||
return fd;
|
||||
}
|
||||
|
||||
public ISyncAdapter getSyncAdapter() throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IContentProvider.descriptor);
|
||||
|
||||
mRemote.transact(IContentProvider.GET_SYNC_ADAPTER_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
ISyncAdapter syncAdapter = ISyncAdapter.Stub.asInterface(reply.readStrongBinder());
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return syncAdapter;
|
||||
}
|
||||
|
||||
private IBinder mRemote;
|
||||
}
|
||||
|
||||
|
||||
@@ -589,6 +589,46 @@ public abstract class ContentResolver {
|
||||
return acquireProvider(mContext, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
|
||||
* that services the content at uri, starting the provider if necessary. Returns
|
||||
* null if there is no provider associated wih the uri. The caller must indicate that they are
|
||||
* done with the provider by calling {@link ContentProviderClient#release} which will allow
|
||||
* the system to release the provider it it determines that there is no other reason for
|
||||
* keeping it active.
|
||||
* @param uri specifies which provider should be acquired
|
||||
* @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
|
||||
* that services the content at uri or null if there isn't one.
|
||||
*/
|
||||
public final ContentProviderClient acquireContentProviderClient(Uri uri) {
|
||||
IContentProvider provider = acquireProvider(uri);
|
||||
if (provider != null) {
|
||||
return new ContentProviderClient(this, provider);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
|
||||
* with the authority of name, starting the provider if necessary. Returns
|
||||
* null if there is no provider associated wih the uri. The caller must indicate that they are
|
||||
* done with the provider by calling {@link ContentProviderClient#release} which will allow
|
||||
* the system to release the provider it it determines that there is no other reason for
|
||||
* keeping it active.
|
||||
* @param name specifies which provider should be acquired
|
||||
* @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
|
||||
* with the authority of name or null if there isn't one.
|
||||
*/
|
||||
public final ContentProviderClient acquireContentProviderClient(String name) {
|
||||
IContentProvider provider = acquireProvider(name);
|
||||
if (provider != null) {
|
||||
return new ContentProviderClient(this, provider);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an observer class that gets callbacks when data identified by a
|
||||
* given content URI changes.
|
||||
|
||||
@@ -55,7 +55,6 @@ public interface IContentProvider extends IInterface {
|
||||
throws RemoteException, FileNotFoundException;
|
||||
public AssetFileDescriptor openAssetFile(Uri url, String mode)
|
||||
throws RemoteException, FileNotFoundException;
|
||||
public ISyncAdapter getSyncAdapter() throws RemoteException;
|
||||
|
||||
/* IPC constants */
|
||||
static final String descriptor = "android.content.IContentProvider";
|
||||
@@ -65,7 +64,6 @@ public interface IContentProvider extends IInterface {
|
||||
static final int INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
|
||||
static final int DELETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
|
||||
static final int UPDATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
|
||||
static final int GET_SYNC_ADAPTER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10;
|
||||
static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12;
|
||||
static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13;
|
||||
static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
|
||||
|
||||
@@ -43,9 +43,9 @@ public abstract class SyncAdapter {
|
||||
Transport mTransport = new Transport();
|
||||
|
||||
/**
|
||||
* Get the Transport object. (note this is package private).
|
||||
* Get the Transport object.
|
||||
*/
|
||||
final ISyncAdapter getISyncAdapter()
|
||||
public final ISyncAdapter getISyncAdapter()
|
||||
{
|
||||
return mTransport;
|
||||
}
|
||||
|
||||
57
core/java/android/content/SyncAdapterType.java
Normal file
57
core/java/android/content/SyncAdapterType.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.content;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Value type that represents a SyncAdapterType. This object overrides {@link #equals} and
|
||||
* {@link #hashCode}, making it suitable for use as the key of a {@link java.util.Map}
|
||||
*/
|
||||
public class SyncAdapterType {
|
||||
public final String authority;
|
||||
public final String accountType;
|
||||
|
||||
public SyncAdapterType(String authority, String accountType) {
|
||||
if (TextUtils.isEmpty(authority)) {
|
||||
throw new IllegalArgumentException("the authority must not be empty: " + authority);
|
||||
}
|
||||
if (TextUtils.isEmpty(accountType)) {
|
||||
throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
|
||||
}
|
||||
this.authority = authority;
|
||||
this.accountType = accountType;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof SyncAdapterType)) return false;
|
||||
final SyncAdapterType other = (SyncAdapterType)o;
|
||||
return authority.equals(other.authority) && accountType.equals(other.accountType);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
result = 31 * result + authority.hashCode();
|
||||
result = 31 * result + accountType.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "SyncAdapterType {name=" + authority + ", type=" + accountType + "}";
|
||||
}
|
||||
}
|
||||
77
core/java/android/content/SyncAdaptersCache.java
Normal file
77
core/java/android/content/SyncAdaptersCache.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.content;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.RegisteredServicesCache;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.util.Log;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Xml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.android.collect.Maps;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* A cache of services that export the {@link android.content.ISyncAdapter} interface.
|
||||
* @hide
|
||||
*/
|
||||
/* package private */ class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> {
|
||||
private static final String TAG = "Account";
|
||||
|
||||
private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
|
||||
private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
|
||||
private static final String ATTRIBUTES_NAME = "sync-adapter";
|
||||
|
||||
SyncAdaptersCache(Context context) {
|
||||
super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME);
|
||||
}
|
||||
|
||||
public SyncAdapterType parseServiceAttributes(AttributeSet attrs) {
|
||||
TypedArray sa = mContext.getResources().obtainAttributes(attrs,
|
||||
com.android.internal.R.styleable.SyncAdapter);
|
||||
try {
|
||||
final String authority =
|
||||
sa.getString(com.android.internal.R.styleable.SyncAdapter_contentAuthority);
|
||||
final String accountType =
|
||||
sa.getString(com.android.internal.R.styleable.SyncAdapter_accountType);
|
||||
if (authority == null || accountType == null) {
|
||||
return null;
|
||||
}
|
||||
return new SyncAdapterType(authority, accountType);
|
||||
} finally {
|
||||
sa.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,8 @@ import android.app.PendingIntent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.RegisteredServicesCache;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.ConnectivityManager;
|
||||
@@ -54,7 +54,6 @@ import android.os.SystemProperties;
|
||||
import android.provider.Sync;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Sync.History;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
import android.util.Config;
|
||||
@@ -70,7 +69,6 @@ import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -79,6 +77,8 @@ import java.util.PriorityQueue;
|
||||
import java.util.Random;
|
||||
import java.util.Observer;
|
||||
import java.util.Observable;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
@@ -124,7 +124,6 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
|
||||
|
||||
private Context mContext;
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
private String mStatusText = "";
|
||||
private long mHeartbeatTime = 0;
|
||||
@@ -157,10 +156,11 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
private final PendingIntent mSyncAlarmIntent;
|
||||
private final PendingIntent mSyncPollAlarmIntent;
|
||||
|
||||
private final SyncAdaptersCache mSyncAdapters;
|
||||
|
||||
private BroadcastReceiver mStorageIntentReceiver =
|
||||
new BroadcastReceiver() {
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
ensureContentResolver();
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
@@ -294,6 +294,8 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
|
||||
mPackageManager = null;
|
||||
|
||||
mSyncAdapters = new SyncAdaptersCache(mContext);
|
||||
|
||||
mSyncAlarmIntent = PendingIntent.getBroadcast(
|
||||
mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
|
||||
|
||||
@@ -467,12 +469,6 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
return mSyncSettings;
|
||||
}
|
||||
|
||||
private void ensureContentResolver() {
|
||||
if (mContentResolver == null) {
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureAlarmService() {
|
||||
if (mAlarmService == null) {
|
||||
mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
|
||||
@@ -590,20 +586,33 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
source = Sync.History.SOURCE_SERVER;
|
||||
}
|
||||
|
||||
List<String> names = new ArrayList<String>();
|
||||
List<ProviderInfo> providers = new ArrayList<ProviderInfo>();
|
||||
populateProvidersList(url, names, providers);
|
||||
// compile a list of authorities that have sync adapters
|
||||
// for each authority sync each account that matches a sync adapter
|
||||
Set<String> syncableAuthorities = new HashSet<String>();
|
||||
for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
|
||||
mSyncAdapters.getAllServices()) {
|
||||
syncableAuthorities.add(syncAdapter.type.authority);
|
||||
}
|
||||
|
||||
final int numProviders = providers.size();
|
||||
for (int i = 0; i < numProviders; i++) {
|
||||
if (!providers.get(i).isSyncable) continue;
|
||||
final String name = names.get(i);
|
||||
// if the url was specified then replace the list of authorities with just this authority
|
||||
// or clear it if this authority isn't syncable
|
||||
if (url != null) {
|
||||
boolean isSyncable = syncableAuthorities.contains(url.getAuthority());
|
||||
syncableAuthorities.clear();
|
||||
if (isSyncable) syncableAuthorities.add(url.getAuthority());
|
||||
}
|
||||
|
||||
for (String authority : syncableAuthorities) {
|
||||
for (Account account : accounts) {
|
||||
scheduleSyncOperation(new SyncOperation(account, source, name, extras, delay));
|
||||
// TODO: remove this when Calendar supports multiple accounts. Until then
|
||||
// pretend that only the first account exists when syncing calendar.
|
||||
if ("calendar".equals(name)) {
|
||||
break;
|
||||
if (mSyncAdapters.getServiceInfo(new SyncAdapterType(authority, account.mType))
|
||||
!= null) {
|
||||
scheduleSyncOperation(
|
||||
new SyncOperation(account, source, authority, extras, delay));
|
||||
// TODO: remove this when Calendar supports multiple accounts. Until then
|
||||
// pretend that only the first account exists when syncing calendar.
|
||||
if ("calendar".equals(authority)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -613,32 +622,6 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
mStatusText = message;
|
||||
}
|
||||
|
||||
private void populateProvidersList(Uri url, List<String> names, List<ProviderInfo> providers) {
|
||||
try {
|
||||
final IPackageManager packageManager = getPackageManager();
|
||||
if (url == null) {
|
||||
packageManager.querySyncProviders(names, providers);
|
||||
} else {
|
||||
final String authority = url.getAuthority();
|
||||
ProviderInfo info = packageManager.resolveContentProvider(url.getAuthority(), 0);
|
||||
if (info != null) {
|
||||
// only set this provider if the requested authority is the primary authority
|
||||
String[] providerNames = info.authority.split(";");
|
||||
if (url.getAuthority().equals(providerNames[0])) {
|
||||
names.add(authority);
|
||||
providers.add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// we should really never get this, but if we do then clear the lists, which
|
||||
// will result in the dropping of the sync request
|
||||
Log.e(TAG, "error trying to get the ProviderInfo for " + url, ex);
|
||||
names.clear();
|
||||
providers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void scheduleLocalSync(Uri url) {
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
|
||||
@@ -672,8 +655,7 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
|
||||
public void updateHeartbeatTime() {
|
||||
mHeartbeatTime = SystemClock.elapsedRealtime();
|
||||
ensureContentResolver();
|
||||
mContentResolver.notifyChange(Sync.Active.CONTENT_URI,
|
||||
mContext.getContentResolver().notifyChange(Sync.Active.CONTENT_URI,
|
||||
null /* this change wasn't made through an observer */);
|
||||
}
|
||||
|
||||
@@ -738,8 +720,7 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
}
|
||||
|
||||
// Cap the delay
|
||||
ensureContentResolver();
|
||||
long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContentResolver,
|
||||
long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContext.getContentResolver(),
|
||||
Settings.Gservices.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
|
||||
DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
|
||||
if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
|
||||
@@ -954,21 +935,19 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
class ActiveSyncContext extends ISyncContext.Stub {
|
||||
class ActiveSyncContext extends ISyncContext.Stub implements ServiceConnection {
|
||||
final SyncOperation mSyncOperation;
|
||||
final long mHistoryRowId;
|
||||
final IContentProvider mContentProvider;
|
||||
final ISyncAdapter mSyncAdapter;
|
||||
ISyncAdapter mSyncAdapter;
|
||||
final long mStartTime;
|
||||
long mTimeoutStartTime;
|
||||
|
||||
public ActiveSyncContext(SyncOperation syncOperation, IContentProvider contentProvider,
|
||||
ISyncAdapter syncAdapter, long historyRowId) {
|
||||
public ActiveSyncContext(SyncOperation syncOperation,
|
||||
long historyRowId) {
|
||||
super();
|
||||
mSyncOperation = syncOperation;
|
||||
mHistoryRowId = historyRowId;
|
||||
mContentProvider = contentProvider;
|
||||
mSyncAdapter = syncAdapter;
|
||||
mSyncAdapter = null;
|
||||
mStartTime = SystemClock.elapsedRealtime();
|
||||
mTimeoutStartTime = mStartTime;
|
||||
}
|
||||
@@ -994,6 +973,37 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
.append(", syncOperation ").append(mSyncOperation);
|
||||
}
|
||||
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
Message msg = mSyncHandler.obtainMessage();
|
||||
msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
|
||||
msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service));
|
||||
mSyncHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
Message msg = mSyncHandler.obtainMessage();
|
||||
msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED;
|
||||
msg.obj = new ServiceConnectionData(this, null);
|
||||
mSyncHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
|
||||
}
|
||||
Intent intent = new Intent();
|
||||
intent.setAction("android.content.SyncAdapter");
|
||||
intent.setComponent(info.componentName);
|
||||
return mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
void unBindFromSyncAdapter() {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
|
||||
}
|
||||
mContext.unbindService(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -1010,6 +1020,11 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
dumpSyncHistory(sb);
|
||||
}
|
||||
pw.println(sb.toString());
|
||||
|
||||
pw.println("SyncAdapters:");
|
||||
for (RegisteredServicesCache.ServiceInfo info : mSyncAdapters.getAllServices()) {
|
||||
pw.println(" " + info);
|
||||
}
|
||||
}
|
||||
|
||||
protected void dumpSyncState(StringBuilder sb) {
|
||||
@@ -1277,6 +1292,15 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceConnectionData {
|
||||
public final ActiveSyncContext activeSyncContext;
|
||||
public final ISyncAdapter syncAdapter;
|
||||
ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) {
|
||||
this.activeSyncContext = activeSyncContext;
|
||||
this.syncAdapter = syncAdapter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles SyncOperation Messages that are posted to the associated
|
||||
* HandlerThread.
|
||||
@@ -1286,6 +1310,8 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
private static final int MESSAGE_SYNC_FINISHED = 1;
|
||||
private static final int MESSAGE_SYNC_ALARM = 2;
|
||||
private static final int MESSAGE_CHECK_ALARMS = 3;
|
||||
private static final int MESSAGE_SERVICE_CONNECTED = 4;
|
||||
private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
|
||||
|
||||
public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
|
||||
private Long mAlarmScheduleTime = null;
|
||||
@@ -1357,6 +1383,53 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
runStateIdle();
|
||||
break;
|
||||
|
||||
case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
|
||||
ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
|
||||
+ msgData.activeSyncContext
|
||||
+ " active is " + mActiveSyncContext);
|
||||
}
|
||||
// check that this isn't an old message
|
||||
if (mActiveSyncContext == msgData.activeSyncContext) {
|
||||
runBoundToSyncAdapter(msgData.syncAdapter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
|
||||
ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
|
||||
+ msgData.activeSyncContext
|
||||
+ " active is " + mActiveSyncContext);
|
||||
}
|
||||
// check that this isn't an old message
|
||||
if (mActiveSyncContext == msgData.activeSyncContext) {
|
||||
// cancel the sync if we have a syncadapter, which means one is
|
||||
// outstanding
|
||||
if (mActiveSyncContext.mSyncAdapter != null) {
|
||||
try {
|
||||
mActiveSyncContext.mSyncAdapter.cancelSync();
|
||||
} catch (RemoteException e) {
|
||||
// we don't need to retry this in this case
|
||||
}
|
||||
}
|
||||
|
||||
// pretend that the sync failed with an IOException,
|
||||
// which is a soft error
|
||||
SyncResult syncResult = new SyncResult();
|
||||
syncResult.stats.numIoExceptions++;
|
||||
runSyncFinishedOrCanceled(syncResult);
|
||||
|
||||
// since we are no longer syncing, check if it is time to start a new
|
||||
// sync
|
||||
runStateIdle();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SyncHandler.MESSAGE_SYNC_ALARM: {
|
||||
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
if (isLoggable) {
|
||||
@@ -1542,67 +1615,68 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
mSyncQueue.popHead();
|
||||
}
|
||||
|
||||
String providerName = syncOperation.authority;
|
||||
ensureContentResolver();
|
||||
IContentProvider contentProvider;
|
||||
|
||||
// acquire the provider and update the sync history
|
||||
try {
|
||||
contentProvider = mContentResolver.acquireProvider(providerName);
|
||||
if (contentProvider == null) {
|
||||
Log.e(TAG, "Provider " + providerName + " doesn't exist");
|
||||
return;
|
||||
// connect to the sync adapter
|
||||
SyncAdapterType syncAdapterType = new SyncAdapterType(syncOperation.authority,
|
||||
syncOperation.account.mType);
|
||||
RegisteredServicesCache.ServiceInfo syncAdapterInfo =
|
||||
mSyncAdapters.getServiceInfo(syncAdapterType);
|
||||
if (syncAdapterInfo == null) {
|
||||
if (Config.LOGD) {
|
||||
Log.d(TAG, "can't find a sync adapter for " + syncAdapterType);
|
||||
}
|
||||
if (contentProvider.getSyncAdapter() == null) {
|
||||
Log.e(TAG, "Provider " + providerName + " isn't syncable, " + contentProvider);
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException remoteExc) {
|
||||
Log.e(TAG, "Caught a RemoteException while preparing for sync, rescheduling "
|
||||
+ syncOperation, remoteExc);
|
||||
rescheduleWithDelay(syncOperation);
|
||||
return;
|
||||
} catch (RuntimeException exc) {
|
||||
Log.e(TAG, "Caught a RuntimeException while validating sync of " + providerName,
|
||||
exc);
|
||||
runStateIdle();
|
||||
return;
|
||||
}
|
||||
|
||||
final long historyRowId = insertStartSyncEvent(syncOperation);
|
||||
|
||||
try {
|
||||
ISyncAdapter syncAdapter = contentProvider.getSyncAdapter();
|
||||
ActiveSyncContext activeSyncContext = new ActiveSyncContext(syncOperation,
|
||||
contentProvider, syncAdapter, historyRowId);
|
||||
mSyncWakeLock.acquire();
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, "starting sync of " + syncOperation);
|
||||
}
|
||||
syncAdapter.startSync(activeSyncContext, syncOperation.account,
|
||||
syncOperation.extras);
|
||||
mActiveSyncContext = activeSyncContext;
|
||||
ActiveSyncContext activeSyncContext =
|
||||
new ActiveSyncContext(syncOperation, insertStartSyncEvent(syncOperation));
|
||||
mActiveSyncContext = activeSyncContext;
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "runStateIdle: setting mActiveSyncContext to " + mActiveSyncContext);
|
||||
}
|
||||
mSyncStorageEngine.setActiveSync(mActiveSyncContext);
|
||||
if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) {
|
||||
Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
|
||||
mActiveSyncContext = null;
|
||||
mSyncStorageEngine.setActiveSync(mActiveSyncContext);
|
||||
runStateIdle();
|
||||
return;
|
||||
}
|
||||
|
||||
mSyncWakeLock.acquire();
|
||||
// no need to schedule an alarm, as that will be done by our caller.
|
||||
|
||||
// the next step will occur when we get either a timeout or a
|
||||
// MESSAGE_SERVICE_CONNECTED or MESSAGE_SERVICE_DISCONNECTED message
|
||||
}
|
||||
|
||||
private void runBoundToSyncAdapter(ISyncAdapter syncAdapter) {
|
||||
mActiveSyncContext.mSyncAdapter = syncAdapter;
|
||||
final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
|
||||
try {
|
||||
syncAdapter.startSync(mActiveSyncContext, syncOperation.account,
|
||||
syncOperation.extras);
|
||||
} catch (RemoteException remoteExc) {
|
||||
if (Config.LOGD) {
|
||||
Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc);
|
||||
}
|
||||
mActiveSyncContext.unBindFromSyncAdapter();
|
||||
mActiveSyncContext = null;
|
||||
mSyncStorageEngine.setActiveSync(mActiveSyncContext);
|
||||
rescheduleWithDelay(syncOperation);
|
||||
} catch (RuntimeException exc) {
|
||||
mActiveSyncContext.unBindFromSyncAdapter();
|
||||
mActiveSyncContext = null;
|
||||
mSyncStorageEngine.setActiveSync(mActiveSyncContext);
|
||||
Log.e(TAG, "Caught a RuntimeException while starting the sync " + syncOperation,
|
||||
exc);
|
||||
}
|
||||
|
||||
// no need to schedule an alarm, as that will be done by our caller.
|
||||
}
|
||||
|
||||
private void runSyncFinishedOrCanceled(SyncResult syncResult) {
|
||||
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
if (isLoggable) Log.v(TAG, "runSyncFinishedOrCanceled");
|
||||
ActiveSyncContext activeSyncContext = mActiveSyncContext;
|
||||
final ActiveSyncContext activeSyncContext = mActiveSyncContext;
|
||||
mActiveSyncContext = null;
|
||||
mSyncStorageEngine.setActiveSync(mActiveSyncContext);
|
||||
|
||||
@@ -1642,10 +1716,12 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
Log.v(TAG, "runSyncFinishedOrCanceled: is a cancel: operation "
|
||||
+ syncOperation);
|
||||
}
|
||||
try {
|
||||
activeSyncContext.mSyncAdapter.cancelSync();
|
||||
} catch (RemoteException e) {
|
||||
// we don't need to retry this in this case
|
||||
if (activeSyncContext.mSyncAdapter != null) {
|
||||
try {
|
||||
activeSyncContext.mSyncAdapter.cancelSync();
|
||||
} catch (RemoteException e) {
|
||||
// we don't need to retry this in this case
|
||||
}
|
||||
}
|
||||
historyMessage = History.MESG_CANCELED;
|
||||
downstreamActivity = 0;
|
||||
@@ -1655,7 +1731,7 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
|
||||
upstreamActivity, downstreamActivity, elapsedTime);
|
||||
|
||||
mContentResolver.releaseProvider(activeSyncContext.mContentProvider);
|
||||
activeSyncContext.unBindFromSyncAdapter();
|
||||
|
||||
if (syncResult != null && syncResult.tooManyDeletions) {
|
||||
installHandleTooManyDeletesNotification(syncOperation.account,
|
||||
|
||||
@@ -33,6 +33,16 @@ import java.util.Map;
|
||||
public abstract class SyncableContentProvider extends ContentProvider {
|
||||
protected abstract boolean isTemporary();
|
||||
|
||||
private volatile TempProviderSyncAdapter mTempProviderSyncAdapter;
|
||||
|
||||
public void setTempProviderSyncAdapter(TempProviderSyncAdapter syncAdapter) {
|
||||
mTempProviderSyncAdapter = syncAdapter;
|
||||
}
|
||||
|
||||
public TempProviderSyncAdapter getTempProviderSyncAdapter() {
|
||||
return mTempProviderSyncAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close resources that must be closed. You must call this to properly release
|
||||
* the resources used by the SyncableContentProvider.
|
||||
|
||||
@@ -119,6 +119,7 @@ interface IPackageManager {
|
||||
* providers that can sync.
|
||||
* @param outInfo Filled in with a list of the ProviderInfo for each
|
||||
* name in 'outNames'.
|
||||
* @deprecated
|
||||
*/
|
||||
void querySyncProviders(inout List<String> outNames,
|
||||
inout List<ProviderInfo> outInfo);
|
||||
|
||||
@@ -65,7 +65,11 @@ public final class ProviderInfo extends ComponentInfo
|
||||
* running in the same process. Higher goes first. */
|
||||
public int initOrder = 0;
|
||||
|
||||
/** Whether or not this provider is syncable. */
|
||||
/**
|
||||
* Whether or not this provider is syncable.
|
||||
* @deprecated This flag is now being ignored. The current way to make a provider
|
||||
* syncable is to provide a SyncAdapter service for a given provider/account type.
|
||||
*/
|
||||
public boolean isSyncable = false;
|
||||
|
||||
public ProviderInfo() {
|
||||
|
||||
233
core/java/android/content/pm/RegisteredServicesCache.java
Normal file
233
core/java/android/content/pm/RegisteredServicesCache.java
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ComponentName;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.util.Log;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Xml;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.android.collect.Maps;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* A cache of registered services. This cache
|
||||
* is built by interrogating the {@link PackageManager} and is updated as packages are added,
|
||||
* removed and changed. The services are referred to by type V and
|
||||
* are made available via the {@link #getServiceInfo} method.
|
||||
* @hide
|
||||
*/
|
||||
public abstract class RegisteredServicesCache<V> {
|
||||
private static final String TAG = "PackageManager";
|
||||
|
||||
public final Context mContext;
|
||||
private final String mInterfaceName;
|
||||
private final String mMetaDataName;
|
||||
private final String mAttributesName;
|
||||
|
||||
// no need to be synchronized since the map is never changed once mService is written
|
||||
private volatile Map<V, ServiceInfo<V>> mServices;
|
||||
|
||||
// synchronized on "this"
|
||||
private BroadcastReceiver mReceiver = null;
|
||||
|
||||
public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
|
||||
String attributeName) {
|
||||
mContext = context;
|
||||
mInterfaceName = interfaceName;
|
||||
mMetaDataName = metaDataName;
|
||||
mAttributesName = attributeName;
|
||||
}
|
||||
|
||||
public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
||||
getAllServices();
|
||||
Map<V, ServiceInfo<V>> services = mServices;
|
||||
fout.println("RegisteredServicesCache: " + services.size() + " services");
|
||||
for (ServiceInfo info : services.values()) {
|
||||
fout.println(" " + info);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean maybeRegisterForPackageChanges() {
|
||||
synchronized (this) {
|
||||
if (mReceiver == null) {
|
||||
synchronized (this) {
|
||||
mReceiver = new BroadcastReceiver() {
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mServices = generateServicesMap();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
mContext.registerReceiver(mReceiver, intentFilter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeUnregisterForPackageChanges() {
|
||||
synchronized (this) {
|
||||
if (mReceiver != null) {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
mReceiver = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Value type that describes a Service. The information within can be used
|
||||
* to bind to the service.
|
||||
*/
|
||||
public static class ServiceInfo<V> {
|
||||
public final V type;
|
||||
public final ComponentName componentName;
|
||||
|
||||
private ServiceInfo(V type, ComponentName componentName) {
|
||||
this.type = type;
|
||||
this.componentName = componentName;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ServiceInfo: " + type + ", " + componentName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for the registered authenticators.
|
||||
* @param type the account type of the authenticator
|
||||
* @return the AuthenticatorInfo that matches the account type or null if none is present
|
||||
*/
|
||||
public ServiceInfo getServiceInfo(V type) {
|
||||
if (mServices == null) {
|
||||
maybeRegisterForPackageChanges();
|
||||
mServices = generateServicesMap();
|
||||
}
|
||||
return mServices.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
|
||||
* registered authenticators.
|
||||
*/
|
||||
public Collection<ServiceInfo<V>> getAllServices() {
|
||||
if (mServices == null) {
|
||||
maybeRegisterForPackageChanges();
|
||||
mServices = generateServicesMap();
|
||||
}
|
||||
return Collections.unmodifiableCollection(mServices.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the monitoring of package additions, removals and changes.
|
||||
*/
|
||||
public void close() {
|
||||
maybeUnregisterForPackageChanges();
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
synchronized (this) {
|
||||
if (mReceiver != null) {
|
||||
Log.e(TAG, "RegisteredServicesCache finalized without being closed");
|
||||
}
|
||||
}
|
||||
close();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
private Map<V, ServiceInfo<V>> generateServicesMap() {
|
||||
Map<V, ServiceInfo<V>> services = Maps.newHashMap();
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
|
||||
List<ResolveInfo> resolveInfos =
|
||||
pm.queryIntentServices(new Intent(mInterfaceName), PackageManager.GET_META_DATA);
|
||||
|
||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||
try {
|
||||
ServiceInfo<V> info = parseServiceInfo(resolveInfo);
|
||||
if (info != null) {
|
||||
services.put(info.type, info);
|
||||
} else {
|
||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString());
|
||||
}
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
|
||||
throws XmlPullParserException, IOException {
|
||||
android.content.pm.ServiceInfo si = service.serviceInfo;
|
||||
ComponentName componentName = new ComponentName(si.packageName, si.name);
|
||||
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
|
||||
XmlResourceParser parser = null;
|
||||
try {
|
||||
parser = si.loadXmlMetaData(pm, mMetaDataName);
|
||||
if (parser == null) {
|
||||
throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
|
||||
}
|
||||
|
||||
AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
|
||||
int type;
|
||||
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& type != XmlPullParser.START_TAG) {
|
||||
}
|
||||
|
||||
String nodeName = parser.getName();
|
||||
if (!mAttributesName.equals(nodeName)) {
|
||||
throw new XmlPullParserException(
|
||||
"Meta-data does not start with " + mAttributesName + " tag");
|
||||
}
|
||||
|
||||
V v = parseServiceAttributes(attrs);
|
||||
if (v == null) {
|
||||
return null;
|
||||
}
|
||||
return new ServiceInfo<V>(v, componentName);
|
||||
} finally {
|
||||
if (parser != null) parser.close();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract V parseServiceAttributes(AttributeSet attrs);
|
||||
}
|
||||
@@ -3235,6 +3235,19 @@
|
||||
<attr name="accountType" format="string"/>
|
||||
</declare-styleable>
|
||||
|
||||
<!-- =============================== -->
|
||||
<!-- Accounts package class attributes -->
|
||||
<!-- =============================== -->
|
||||
|
||||
<!-- Use <code>account-authenticator</code> as the root tag of the XML resource that
|
||||
describes an account authenticator.
|
||||
-->
|
||||
<declare-styleable name="SyncAdapter">
|
||||
<!-- the authority of a content provider. -->
|
||||
<attr name="contentAuthority" format="string"/>
|
||||
<attr name="accountType"/>
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -1081,7 +1081,7 @@
|
||||
|
||||
<!-- Drawable to use as a background for a taller version of the titlebar -->
|
||||
<public type="drawable" name="title_bar_tall" id="0x010800a6" />
|
||||
|
||||
|
||||
<public type="integer" name="config_shortAnimTime" id="0x010e0000" />
|
||||
<public type="integer" name="config_mediumAnimTime" id="0x010e0001" />
|
||||
<public type="integer" name="config_longAnimTime" id="0x010e0002" />
|
||||
@@ -1109,4 +1109,5 @@
|
||||
<eat-comment />
|
||||
|
||||
<public type="attr" name="accountType" id="0x0101026d" />
|
||||
<public type="attr" name="contentAuthority" id="0x0101026e" />
|
||||
</resources>
|
||||
|
||||
@@ -1616,6 +1616,9 @@ class PackageManagerService extends IPackageManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public void querySyncProviders(List outNames, List outInfo) {
|
||||
synchronized (mPackages) {
|
||||
Iterator<Map.Entry<String, PackageParser.Provider>> i
|
||||
|
||||
@@ -61,11 +61,6 @@ public class MockContentProvider implements IContentProvider {
|
||||
throw new UnsupportedOperationException("unimplemented mock method");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public ISyncAdapter getSyncAdapter() throws RemoteException {
|
||||
throw new UnsupportedOperationException("unimplemented mock method");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String getType(Uri url) throws RemoteException {
|
||||
throw new UnsupportedOperationException("unimplemented mock method");
|
||||
|
||||
Reference in New Issue
Block a user