am e871be66: Merge change I735f4553 into eclair-mr2
Merge commit 'e871be666b80d3f6a9a9a44ee400ab661be212e4' into eclair-mr2-plus-aosp * commit 'e871be666b80d3f6a9a9a44ee400ab661be212e4': Make the RegisteredSErvices Cache not allow the registered service for a
This commit is contained in:
@@ -18,10 +18,16 @@ package android.accounts;
|
|||||||
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.RegisteredServicesCache;
|
import android.content.pm.RegisteredServicesCache;
|
||||||
|
import android.content.pm.XmlSerializerAndParser;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache of services that export the {@link IAccountAuthenticator} interface. This cache
|
* A cache of services that export the {@link IAccountAuthenticator} interface. This cache
|
||||||
@@ -33,10 +39,12 @@ import android.text.TextUtils;
|
|||||||
/* package private */ class AccountAuthenticatorCache
|
/* package private */ class AccountAuthenticatorCache
|
||||||
extends RegisteredServicesCache<AuthenticatorDescription> {
|
extends RegisteredServicesCache<AuthenticatorDescription> {
|
||||||
private static final String TAG = "Account";
|
private static final String TAG = "Account";
|
||||||
|
private static final MySerializer sSerializer = new MySerializer();
|
||||||
|
|
||||||
public AccountAuthenticatorCache(Context context) {
|
public AccountAuthenticatorCache(Context context) {
|
||||||
super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT,
|
super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT,
|
||||||
AccountManager.AUTHENTICATOR_META_DATA_NAME, AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME);
|
AccountManager.AUTHENTICATOR_META_DATA_NAME,
|
||||||
|
AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticatorDescription parseServiceAttributes(String packageName, AttributeSet attrs) {
|
public AuthenticatorDescription parseServiceAttributes(String packageName, AttributeSet attrs) {
|
||||||
@@ -62,4 +70,16 @@ import android.text.TextUtils;
|
|||||||
sa.recycle();
|
sa.recycle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MySerializer implements XmlSerializerAndParser<AuthenticatorDescription> {
|
||||||
|
public void writeAsXml(AuthenticatorDescription item, XmlSerializer out)
|
||||||
|
throws IOException {
|
||||||
|
out.attribute(null, "type", item.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticatorDescription createFromXml(XmlPullParser parser)
|
||||||
|
throws IOException, XmlPullParserException {
|
||||||
|
return AuthenticatorDescription.newKey(parser.getAttributeValue(null, "type"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ import com.android.internal.telephony.TelephonyIntents;
|
|||||||
*/
|
*/
|
||||||
public class AccountManagerService
|
public class AccountManagerService
|
||||||
extends IAccountManager.Stub
|
extends IAccountManager.Stub
|
||||||
implements RegisteredServicesCacheListener {
|
implements RegisteredServicesCacheListener<AuthenticatorDescription> {
|
||||||
private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
|
private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
|
||||||
|
|
||||||
private static final String NO_BROADCAST_FLAG = "nobroadcast";
|
private static final String NO_BROADCAST_FLAG = "nobroadcast";
|
||||||
@@ -221,32 +221,27 @@ public class AccountManagerService
|
|||||||
mMessageHandler = new MessageHandler(mMessageThread.getLooper());
|
mMessageHandler = new MessageHandler(mMessageThread.getLooper());
|
||||||
|
|
||||||
mAuthenticatorCache = new AccountAuthenticatorCache(mContext);
|
mAuthenticatorCache = new AccountAuthenticatorCache(mContext);
|
||||||
mAuthenticatorCache.setListener(this);
|
mAuthenticatorCache.setListener(this, null /* Handler */);
|
||||||
|
|
||||||
mSimWatcher = new SimWatcher(mContext);
|
mSimWatcher = new SimWatcher(mContext);
|
||||||
sThis.set(this);
|
sThis.set(this);
|
||||||
|
|
||||||
onRegisteredServicesCacheChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRegisteredServicesCacheChanged() {
|
public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
|
||||||
boolean accountDeleted = false;
|
boolean accountDeleted = false;
|
||||||
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
||||||
Cursor cursor = db.query(TABLE_ACCOUNTS,
|
Cursor cursor = db.query(TABLE_ACCOUNTS,
|
||||||
new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
|
new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
|
||||||
null, null, null, null, null);
|
ACCOUNTS_TYPE + "=?", new String[]{desc.type}, null, null, null);
|
||||||
try {
|
try {
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
final long accountId = cursor.getLong(0);
|
final long accountId = cursor.getLong(0);
|
||||||
final String accountType = cursor.getString(1);
|
final String accountType = cursor.getString(1);
|
||||||
final String accountName = cursor.getString(2);
|
final String accountName = cursor.getString(2);
|
||||||
if (mAuthenticatorCache.getServiceInfo(AuthenticatorDescription.newKey(accountType))
|
|
||||||
== null) {
|
|
||||||
Log.d(TAG, "deleting account " + accountName + " because type "
|
Log.d(TAG, "deleting account " + accountName + " because type "
|
||||||
+ accountType + " no longer has a registered authenticator");
|
+ accountType + " no longer has a registered authenticator");
|
||||||
db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
|
db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
|
||||||
accountDeleted= true;
|
accountDeleted = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ public class AuthenticatorDescription implements Parcelable {
|
|||||||
return type.equals(other.type);
|
return type.equals(other.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "AuthenticatorDescription {type=" + type + "}";
|
||||||
|
}
|
||||||
|
|
||||||
/** @inhericDoc */
|
/** @inhericDoc */
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeString(type);
|
dest.writeString(type);
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
|
|||||||
public void onCreate(SQLiteDatabase db) {
|
public void onCreate(SQLiteDatabase db) {
|
||||||
bootstrapDatabase(db);
|
bootstrapDatabase(db);
|
||||||
mSyncState.createDatabase(db);
|
mSyncState.createDatabase(db);
|
||||||
|
ContentResolver.requestSync(null /* all accounts */,
|
||||||
|
mContentUri.getAuthority(), new Bundle());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,9 +17,14 @@
|
|||||||
package android.content;
|
package android.content;
|
||||||
|
|
||||||
import android.content.pm.RegisteredServicesCache;
|
import android.content.pm.RegisteredServicesCache;
|
||||||
|
import android.content.pm.XmlSerializerAndParser;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache of services that export the {@link android.content.ISyncAdapter} interface.
|
* A cache of services that export the {@link android.content.ISyncAdapter} interface.
|
||||||
@@ -31,9 +36,10 @@ import android.util.AttributeSet;
|
|||||||
private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
|
private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
|
||||||
private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
|
private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
|
||||||
private static final String ATTRIBUTES_NAME = "sync-adapter";
|
private static final String ATTRIBUTES_NAME = "sync-adapter";
|
||||||
|
private static final MySerializer sSerializer = new MySerializer();
|
||||||
|
|
||||||
SyncAdaptersCache(Context context) {
|
SyncAdaptersCache(Context context) {
|
||||||
super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME);
|
super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) {
|
public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) {
|
||||||
@@ -57,4 +63,18 @@ import android.util.AttributeSet;
|
|||||||
sa.recycle();
|
sa.recycle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class MySerializer implements XmlSerializerAndParser<SyncAdapterType> {
|
||||||
|
public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException {
|
||||||
|
out.attribute(null, "authority", item.authority);
|
||||||
|
out.attribute(null, "accountType", item.accountType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyncAdapterType createFromXml(XmlPullParser parser)
|
||||||
|
throws IOException, XmlPullParserException {
|
||||||
|
final String authority = parser.getAttributeValue(null, "authority");
|
||||||
|
final String accountType = parser.getAttributeValue(null, "accountType");
|
||||||
|
return SyncAdapterType.newKey(authority, accountType);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,8 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
@@ -29,14 +31,26 @@ import android.util.Xml;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
import com.android.internal.os.AtomicFile;
|
||||||
|
import com.android.internal.util.FastXmlSerializer;
|
||||||
|
|
||||||
import com.google.android.collect.Maps;
|
import com.google.android.collect.Maps;
|
||||||
|
import com.google.android.collect.Lists;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache of registered services. This cache
|
* A cache of registered services. This cache
|
||||||
@@ -52,75 +66,104 @@ public abstract class RegisteredServicesCache<V> {
|
|||||||
private final String mInterfaceName;
|
private final String mInterfaceName;
|
||||||
private final String mMetaDataName;
|
private final String mMetaDataName;
|
||||||
private final String mAttributesName;
|
private final String mAttributesName;
|
||||||
|
private final XmlSerializerAndParser<V> mSerializerAndParser;
|
||||||
|
private final AtomicReference<BroadcastReceiver> mReceiver;
|
||||||
|
|
||||||
public RegisteredServicesCacheListener getListener() {
|
private final Object mServicesLock = new Object();
|
||||||
return mListener;
|
// synchronized on mServicesLock
|
||||||
}
|
private HashMap<V, Integer> mPersistentServices;
|
||||||
|
// synchronized on mServicesLock
|
||||||
|
private Map<V, ServiceInfo<V>> mServices;
|
||||||
|
// synchronized on mServicesLock
|
||||||
|
private boolean mPersistentServicesFileDidNotExist;
|
||||||
|
|
||||||
public void setListener(RegisteredServicesCacheListener listener) {
|
/**
|
||||||
mListener = listener;
|
* This file contains the list of known services. We would like to maintain this forever
|
||||||
}
|
* so we store it as an XML file.
|
||||||
|
*/
|
||||||
|
private final AtomicFile mPersistentServicesFile;
|
||||||
|
|
||||||
private volatile RegisteredServicesCacheListener mListener;
|
// the listener and handler are synchronized on "this" and must be updated together
|
||||||
|
private RegisteredServicesCacheListener<V> mListener;
|
||||||
// no need to be synchronized since the map is never changed once mService is written
|
private Handler mHandler;
|
||||||
volatile Map<V, ServiceInfo<V>> mServices;
|
|
||||||
|
|
||||||
// synchronized on "this"
|
|
||||||
private BroadcastReceiver mReceiver = null;
|
|
||||||
|
|
||||||
public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
|
public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
|
||||||
String attributeName) {
|
String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mInterfaceName = interfaceName;
|
mInterfaceName = interfaceName;
|
||||||
mMetaDataName = metaDataName;
|
mMetaDataName = metaDataName;
|
||||||
mAttributesName = attributeName;
|
mAttributesName = attributeName;
|
||||||
|
mSerializerAndParser = serializerAndParser;
|
||||||
|
|
||||||
|
File dataDir = Environment.getDataDirectory();
|
||||||
|
File systemDir = new File(dataDir, "system");
|
||||||
|
File syncDir = new File(systemDir, "registered_services");
|
||||||
|
mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
|
||||||
|
|
||||||
|
generateServicesMap();
|
||||||
|
|
||||||
|
final BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context1, Intent intent) {
|
||||||
|
generateServicesMap();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
|
||||||
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||||
|
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||||
|
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||||
|
intentFilter.addDataScheme("package");
|
||||||
|
mContext.registerReceiver(receiver, intentFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
||||||
getAllServices();
|
Map<V, ServiceInfo<V>> services;
|
||||||
Map<V, ServiceInfo<V>> services = mServices;
|
synchronized (mServicesLock) {
|
||||||
|
services = mServices;
|
||||||
|
}
|
||||||
fout.println("RegisteredServicesCache: " + services.size() + " services");
|
fout.println("RegisteredServicesCache: " + services.size() + " services");
|
||||||
for (ServiceInfo info : services.values()) {
|
for (ServiceInfo info : services.values()) {
|
||||||
fout.println(" " + info);
|
fout.println(" " + info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean maybeRegisterForPackageChanges() {
|
public RegisteredServicesCacheListener<V> getListener() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (mReceiver == null) {
|
return mListener;
|
||||||
synchronized (this) {
|
|
||||||
mReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
mServices = generateServicesMap();
|
|
||||||
RegisteredServicesCacheListener listener = mListener;
|
|
||||||
if (listener != null) {
|
|
||||||
listener.onRegisteredServicesCacheChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
|
||||||
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
|
||||||
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
|
||||||
intentFilter.addDataScheme("package");
|
|
||||||
mContext.registerReceiver(mReceiver, intentFilter);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeUnregisterForPackageChanges() {
|
public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
|
||||||
|
if (handler == null) {
|
||||||
|
handler = new Handler(mContext.getMainLooper());
|
||||||
|
}
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (mReceiver != null) {
|
mHandler = handler;
|
||||||
mContext.unregisterReceiver(mReceiver);
|
mListener = listener;
|
||||||
mReceiver = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notifyListener(final V type, final boolean removed) {
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
|
||||||
|
}
|
||||||
|
RegisteredServicesCacheListener<V> listener;
|
||||||
|
Handler handler;
|
||||||
|
synchronized (this) {
|
||||||
|
listener = mListener;
|
||||||
|
handler = mHandler;
|
||||||
|
}
|
||||||
|
if (listener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final RegisteredServicesCacheListener<V> listener2 = listener;
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
listener2.onServiceChanged(type, removed);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,7 +183,7 @@ public abstract class RegisteredServicesCache<V> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ServiceInfo: " + type + ", " + componentName;
|
return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,66 +193,181 @@ public abstract class RegisteredServicesCache<V> {
|
|||||||
* @return the AuthenticatorInfo that matches the account type or null if none is present
|
* @return the AuthenticatorInfo that matches the account type or null if none is present
|
||||||
*/
|
*/
|
||||||
public ServiceInfo<V> getServiceInfo(V type) {
|
public ServiceInfo<V> getServiceInfo(V type) {
|
||||||
if (mServices == null) {
|
synchronized (mServicesLock) {
|
||||||
maybeRegisterForPackageChanges();
|
|
||||||
mServices = generateServicesMap();
|
|
||||||
}
|
|
||||||
return mServices.get(type);
|
return mServices.get(type);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
|
* @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
|
||||||
* registered authenticators.
|
* registered authenticators.
|
||||||
*/
|
*/
|
||||||
public Collection<ServiceInfo<V>> getAllServices() {
|
public Collection<ServiceInfo<V>> getAllServices() {
|
||||||
if (mServices == null) {
|
synchronized (mServicesLock) {
|
||||||
maybeRegisterForPackageChanges();
|
|
||||||
mServices = generateServicesMap();
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableCollection(mServices.values());
|
return Collections.unmodifiableCollection(mServices.values());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the monitoring of package additions, removals and changes.
|
* Stops the monitoring of package additions, removals and changes.
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
maybeUnregisterForPackageChanges();
|
final BroadcastReceiver receiver = mReceiver.getAndSet(null);
|
||||||
|
if (receiver != null) {
|
||||||
|
mContext.unregisterReceiver(receiver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
synchronized (this) {
|
if (mReceiver.get() != null) {
|
||||||
if (mReceiver != null) {
|
|
||||||
Log.e(TAG, "RegisteredServicesCache finalized without being closed");
|
Log.e(TAG, "RegisteredServicesCache finalized without being closed");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
close();
|
close();
|
||||||
super.finalize();
|
super.finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<V, ServiceInfo<V>> generateServicesMap() {
|
private boolean inSystemImage(int callerUid) {
|
||||||
Map<V, ServiceInfo<V>> services = Maps.newHashMap();
|
String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
|
||||||
|
for (String name : packages) {
|
||||||
|
try {
|
||||||
|
PackageInfo packageInfo =
|
||||||
|
mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
|
||||||
|
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateServicesMap() {
|
||||||
PackageManager pm = mContext.getPackageManager();
|
PackageManager pm = mContext.getPackageManager();
|
||||||
|
ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
|
||||||
List<ResolveInfo> resolveInfos =
|
List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName),
|
||||||
pm.queryIntentServices(new Intent(mInterfaceName), PackageManager.GET_META_DATA);
|
PackageManager.GET_META_DATA);
|
||||||
|
|
||||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||||
try {
|
try {
|
||||||
ServiceInfo<V> info = parseServiceInfo(resolveInfo);
|
ServiceInfo<V> info = parseServiceInfo(resolveInfo);
|
||||||
if (info != null) {
|
if (info == null) {
|
||||||
services.put(info.type, info);
|
Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
|
||||||
} else {
|
continue;
|
||||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString());
|
|
||||||
}
|
}
|
||||||
|
serviceInfos.add(info);
|
||||||
} catch (XmlPullParserException e) {
|
} catch (XmlPullParserException e) {
|
||||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
|
Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
|
Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return services;
|
synchronized (mServicesLock) {
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.d(TAG, "generateServicesMap: " + mInterfaceName);
|
||||||
|
}
|
||||||
|
if (mPersistentServices == null) {
|
||||||
|
readPersistentServicesLocked();
|
||||||
|
}
|
||||||
|
mServices = Maps.newHashMap();
|
||||||
|
boolean changed = false;
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.d(TAG, "found " + serviceInfos.size() + " services");
|
||||||
|
}
|
||||||
|
for (ServiceInfo<V> info : serviceInfos) {
|
||||||
|
// four cases:
|
||||||
|
// - doesn't exist yet
|
||||||
|
// - add, notify user that it was added
|
||||||
|
// - exists and the UID is the same
|
||||||
|
// - replace, don't notify user
|
||||||
|
// - exists, the UID is different, and the new one is not a system package
|
||||||
|
// - ignore
|
||||||
|
// - exists, the UID is different, and the new one is a system package
|
||||||
|
// - add, notify user that it was added
|
||||||
|
Integer previousUid = mPersistentServices.get(info.type);
|
||||||
|
if (previousUid == null) {
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.d(TAG, "encountered new type: " + info);
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
mServices.put(info.type, info);
|
||||||
|
mPersistentServices.put(info.type, info.uid);
|
||||||
|
if (!mPersistentServicesFileDidNotExist) {
|
||||||
|
notifyListener(info.type, false /* removed */);
|
||||||
|
}
|
||||||
|
} else if (previousUid == info.uid) {
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.d(TAG, "encountered existing type with the same uid: " + info);
|
||||||
|
}
|
||||||
|
mServices.put(info.type, info);
|
||||||
|
} else if (inSystemImage(info.uid)
|
||||||
|
|| !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
if (inSystemImage(info.uid)) {
|
||||||
|
Log.d(TAG, "encountered existing type with a new uid but from"
|
||||||
|
+ " the system: " + info);
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "encountered existing type with a new uid but existing was"
|
||||||
|
+ " removed: " + info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
mServices.put(info.type, info);
|
||||||
|
mPersistentServices.put(info.type, info.uid);
|
||||||
|
notifyListener(info.type, false /* removed */);
|
||||||
|
} else {
|
||||||
|
// ignore
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.d(TAG, "encountered existing type with a new uid, ignoring: " + info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<V> toBeRemoved = Lists.newArrayList();
|
||||||
|
for (V v1 : mPersistentServices.keySet()) {
|
||||||
|
if (!containsType(serviceInfos, v1)) {
|
||||||
|
toBeRemoved.add(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (V v1 : toBeRemoved) {
|
||||||
|
mPersistentServices.remove(v1);
|
||||||
|
changed = true;
|
||||||
|
notifyListener(v1, true /* removed */);
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.d(TAG, "writing updated list of persistent services");
|
||||||
|
}
|
||||||
|
writePersistentServicesLocked();
|
||||||
|
} else {
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
Log.d(TAG, "persistent services did not change, so not writing anything");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mPersistentServicesFileDidNotExist = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) {
|
||||||
|
for (int i = 0, N = serviceInfos.size(); i < N; i++) {
|
||||||
|
if (serviceInfos.get(i).type.equals(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) {
|
||||||
|
for (int i = 0, N = serviceInfos.size(); i < N; i++) {
|
||||||
|
final ServiceInfo<V> serviceInfo = serviceInfos.get(i);
|
||||||
|
if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
|
private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
|
||||||
@@ -252,5 +410,89 @@ public abstract class RegisteredServicesCache<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all sync status back in to the initial engine state.
|
||||||
|
*/
|
||||||
|
private void readPersistentServicesLocked() {
|
||||||
|
mPersistentServices = Maps.newHashMap();
|
||||||
|
if (mSerializerAndParser == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists();
|
||||||
|
if (mPersistentServicesFileDidNotExist) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fis = mPersistentServicesFile.openRead();
|
||||||
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
|
parser.setInput(fis, null);
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
while (eventType != XmlPullParser.START_TAG) {
|
||||||
|
eventType = parser.next();
|
||||||
|
}
|
||||||
|
String tagName = parser.getName();
|
||||||
|
if ("services".equals(tagName)) {
|
||||||
|
eventType = parser.next();
|
||||||
|
do {
|
||||||
|
if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
|
||||||
|
tagName = parser.getName();
|
||||||
|
if ("service".equals(tagName)) {
|
||||||
|
V service = mSerializerAndParser.createFromXml(parser);
|
||||||
|
if (service == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String uidString = parser.getAttributeValue(null, "uid");
|
||||||
|
int uid = Integer.parseInt(uidString);
|
||||||
|
mPersistentServices.put(service, uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventType = parser.next();
|
||||||
|
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Error reading persistent services, starting from scratch", e);
|
||||||
|
} finally {
|
||||||
|
if (fis != null) {
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} catch (java.io.IOException e1) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write all sync status to the sync status file.
|
||||||
|
*/
|
||||||
|
private void writePersistentServicesLocked() {
|
||||||
|
if (mSerializerAndParser == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
try {
|
||||||
|
fos = mPersistentServicesFile.startWrite();
|
||||||
|
XmlSerializer out = new FastXmlSerializer();
|
||||||
|
out.setOutput(fos, "utf-8");
|
||||||
|
out.startDocument(null, true);
|
||||||
|
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
||||||
|
out.startTag(null, "services");
|
||||||
|
for (Map.Entry<V, Integer> service : mPersistentServices.entrySet()) {
|
||||||
|
out.startTag(null, "service");
|
||||||
|
out.attribute(null, "uid", Integer.toString(service.getValue()));
|
||||||
|
mSerializerAndParser.writeAsXml(service.getKey(), out);
|
||||||
|
out.endTag(null, "service");
|
||||||
|
}
|
||||||
|
out.endTag(null, "services");
|
||||||
|
out.endDocument();
|
||||||
|
mPersistentServicesFile.finishWrite(fos);
|
||||||
|
} catch (java.io.IOException e1) {
|
||||||
|
Log.w(TAG, "Error writing accounts", e1);
|
||||||
|
if (fos != null) {
|
||||||
|
mPersistentServicesFile.failWrite(fos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public abstract V parseServiceAttributes(String packageName, AttributeSet attrs);
|
public abstract V parseServiceAttributes(String packageName, AttributeSet attrs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package android.content.pm;
|
package android.content.pm;
|
||||||
|
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener for changes to the set of registered services managed by a RegisteredServicesCache.
|
* Listener for changes to the set of registered services managed by a RegisteredServicesCache.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public interface RegisteredServicesCacheListener {
|
public interface RegisteredServicesCacheListener<V> {
|
||||||
/**
|
/**
|
||||||
* Invoked when the registered services cache changes.
|
* Invoked when a service is registered or changed.
|
||||||
|
* @param type the type of registered service
|
||||||
|
* @param removed true if the service was removed
|
||||||
*/
|
*/
|
||||||
void onRegisteredServicesCacheChanged();
|
void onServiceChanged(V type, boolean removed);
|
||||||
}
|
}
|
||||||
|
|||||||
14
core/java/android/content/pm/XmlSerializerAndParser.java
Normal file
14
core/java/android/content/pm/XmlSerializerAndParser.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package android.content.pm;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public interface XmlSerializerAndParser<T> {
|
||||||
|
void writeAsXml(T item, XmlSerializer out) throws IOException;
|
||||||
|
T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user