Merge change 777 into donut

* changes:
  Rewrite SyncStorageEngine to use flat files and in-memory data structures.
This commit is contained in:
Android (Google) Code Review
2009-05-05 16:17:25 -07:00
25 changed files with 2554 additions and 2069 deletions

View File

@@ -85,8 +85,10 @@ LOCAL_SRC_FILES += \
core/java/android/bluetooth/IBluetoothDevice.aidl \ core/java/android/bluetooth/IBluetoothDevice.aidl \
core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \ core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
core/java/android/bluetooth/IBluetoothHeadset.aidl \ core/java/android/bluetooth/IBluetoothHeadset.aidl \
core/java/android/content/IContentService.aidl \
core/java/android/content/ISyncAdapter.aidl \ core/java/android/content/ISyncAdapter.aidl \
core/java/android/content/ISyncContext.aidl \ core/java/android/content/ISyncContext.aidl \
core/java/android/content/ISyncStatusObserver.aidl \
core/java/android/content/pm/IPackageDataObserver.aidl \ core/java/android/content/pm/IPackageDataObserver.aidl \
core/java/android/content/pm/IPackageDeleteObserver.aidl \ core/java/android/content/pm/IPackageDeleteObserver.aidl \
core/java/android/content/pm/IPackageInstallObserver.aidl \ core/java/android/content/pm/IPackageInstallObserver.aidl \

View File

@@ -92268,6 +92268,19 @@
visibility="public" visibility="public"
> >
</method> </method>
<method name="getBroadcastCookie"
return="java.lang.Object"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="index" type="int">
</parameter>
</method>
<method name="getBroadcastItem" <method name="getBroadcastItem"
return="E" return="E"
abstract="false" abstract="false"
@@ -92305,6 +92318,21 @@
<parameter name="callback" type="E"> <parameter name="callback" type="E">
</parameter> </parameter>
</method> </method>
<method name="onCallbackDied"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="callback" type="E">
</parameter>
<parameter name="cookie" type="java.lang.Object">
</parameter>
</method>
<method name="register" <method name="register"
return="boolean" return="boolean"
abstract="false" abstract="false"
@@ -92318,6 +92346,21 @@
<parameter name="callback" type="E"> <parameter name="callback" type="E">
</parameter> </parameter>
</method> </method>
<method name="register"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="callback" type="E">
</parameter>
<parameter name="cookie" type="java.lang.Object">
</parameter>
</method>
<method name="unregister" <method name="unregister"
return="boolean" return="boolean"
abstract="false" abstract="false"

View File

@@ -0,0 +1,19 @@
/*
* 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;
parcelable ActiveSyncInfo;

View File

@@ -0,0 +1,64 @@
/*
* 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.os.Parcel;
import android.os.Parcelable.Creator;
/** @hide */
public class ActiveSyncInfo {
public final int authorityId;
public final String account;
public final String authority;
public final long startTime;
ActiveSyncInfo(int authorityId, String account, String authority,
long startTime) {
this.authorityId = authorityId;
this.account = account;
this.authority = authority;
this.startTime = startTime;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(authorityId);
parcel.writeString(account);
parcel.writeString(authority);
parcel.writeLong(startTime);
}
ActiveSyncInfo(Parcel parcel) {
authorityId = parcel.readInt();
account = parcel.readString();
authority = parcel.readString();
startTime = parcel.readLong();
}
public static final Creator<ActiveSyncInfo> CREATOR = new Creator<ActiveSyncInfo>() {
public ActiveSyncInfo createFromParcel(Parcel in) {
return new ActiveSyncInfo(in);
}
public ActiveSyncInfo[] newArray(int size) {
return new ActiveSyncInfo[size];
}
};
}

View File

@@ -25,9 +25,13 @@ import android.database.CursorWrapper;
import android.database.IContentObserver; import android.database.IContentObserver;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -85,8 +89,7 @@ public abstract class ContentResolver {
*/ */
public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir"; public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
public ContentResolver(Context context) public ContentResolver(Context context) {
{
mContext = context; mContext = context;
} }
@@ -605,7 +608,7 @@ public abstract class ContentResolver {
ContentObserver observer) ContentObserver observer)
{ {
try { try {
ContentServiceNative.getDefault().registerContentObserver(uri, notifyForDescendents, getContentService().registerContentObserver(uri, notifyForDescendents,
observer.getContentObserver()); observer.getContentObserver());
} catch (RemoteException e) { } catch (RemoteException e) {
} }
@@ -621,7 +624,7 @@ public abstract class ContentResolver {
try { try {
IContentObserver contentObserver = observer.releaseContentObserver(); IContentObserver contentObserver = observer.releaseContentObserver();
if (contentObserver != null) { if (contentObserver != null) {
ContentServiceNative.getDefault().unregisterContentObserver( getContentService().unregisterContentObserver(
contentObserver); contentObserver);
} }
} catch (RemoteException e) { } catch (RemoteException e) {
@@ -651,7 +654,7 @@ public abstract class ContentResolver {
*/ */
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
try { try {
ContentServiceNative.getDefault().notifyChange( getContentService().notifyChange(
uri, observer == null ? null : observer.getContentObserver(), uri, observer == null ? null : observer.getContentObserver(),
observer != null && observer.deliverSelfNotifications(), syncToNetwork); observer != null && observer.deliverSelfNotifications(), syncToNetwork);
} catch (RemoteException e) { } catch (RemoteException e) {
@@ -677,7 +680,7 @@ public abstract class ContentResolver {
public void startSync(Uri uri, Bundle extras) { public void startSync(Uri uri, Bundle extras) {
validateSyncExtrasBundle(extras); validateSyncExtrasBundle(extras);
try { try {
ContentServiceNative.getDefault().startSync(uri, extras); getContentService().startSync(uri, extras);
} catch (RemoteException e) { } catch (RemoteException e) {
} }
} }
@@ -718,7 +721,7 @@ public abstract class ContentResolver {
public void cancelSync(Uri uri) { public void cancelSync(Uri uri) {
try { try {
ContentServiceNative.getDefault().cancelSync(uri); getContentService().cancelSync(uri);
} catch (RemoteException e) { } catch (RemoteException e) {
} }
} }
@@ -779,6 +782,22 @@ public abstract class ContentResolver {
} }
} }
/** @hide */
public static final String CONTENT_SERVICE_NAME = "content";
/** @hide */
public static IContentService getContentService() {
if (sContentService != null) {
return sContentService;
}
IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
if (Config.LOGV) Log.v("ContentService", "default service binder = " + b);
sContentService = IContentService.Stub.asInterface(b);
if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService);
return sContentService;
}
private static IContentService sContentService;
private final Context mContext; private final Context mContext;
private static final String TAG = "ContentResolver"; private static final String TAG = "ContentResolver";
} }

View File

@@ -21,6 +21,7 @@ import android.database.sqlite.SQLiteException;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.util.Config; import android.util.Config;
@@ -34,7 +35,7 @@ import java.util.ArrayList;
/** /**
* {@hide} * {@hide}
*/ */
public final class ContentService extends ContentServiceNative { public final class ContentService extends IContentService.Stub {
private static final String TAG = "ContentService"; private static final String TAG = "ContentService";
private Context mContext; private Context mContext;
private boolean mFactoryTest; private boolean mFactoryTest;
@@ -73,6 +74,21 @@ public final class ContentService extends ContentServiceNative {
} }
} }
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
// The content service only throws security exceptions, so let's
// log all others.
if (!(e instanceof SecurityException)) {
Log.e(TAG, "Content Service Crash", e);
}
throw e;
}
}
/*package*/ ContentService(Context context, boolean factoryTest) { /*package*/ ContentService(Context context, boolean factoryTest) {
mContext = context; mContext = context;
mFactoryTest = factoryTest; mFactoryTest = factoryTest;
@@ -204,9 +220,158 @@ public final class ContentService extends ContentServiceNative {
} }
} }
public boolean getSyncProviderAutomatically(String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().getSyncProviderAutomatically(
null, providerName);
}
} finally {
restoreCallingIdentity(identityToken);
}
return false;
}
public void setSyncProviderAutomatically(String providerName, boolean sync) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.getSyncStorageEngine().setSyncProviderAutomatically(
null, providerName, sync);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
public boolean getListenForNetworkTickles() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().getListenForNetworkTickles();
}
} finally {
restoreCallingIdentity(identityToken);
}
return false;
}
public void setListenForNetworkTickles(boolean flag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.getSyncStorageEngine().setListenForNetworkTickles(flag);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
public boolean isSyncActive(String account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().isSyncActive(
account, authority);
}
} finally {
restoreCallingIdentity(identityToken);
}
return false;
}
public ActiveSyncInfo getActiveSync() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().getActiveSync();
}
} finally {
restoreCallingIdentity(identityToken);
}
return null;
}
public SyncStatusInfo getStatusByAuthority(String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().getStatusByAuthority(
authority);
}
} finally {
restoreCallingIdentity(identityToken);
}
return null;
}
public boolean isAuthorityPending(String account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().isAuthorityPending(
account, authority);
}
} finally {
restoreCallingIdentity(identityToken);
}
return false;
}
public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.getSyncStorageEngine().addStatusChangeListener(
mask, callback);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
public void removeStatusChangeListener(ISyncStatusObserver callback) {
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.getSyncStorageEngine().removeStatusChangeListener(
callback);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
public static IContentService main(Context context, boolean factoryTest) { public static IContentService main(Context context, boolean factoryTest) {
ContentService service = new ContentService(context, factoryTest); ContentService service = new ContentService(context, factoryTest);
ServiceManager.addService("content", service); ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
return service; return service;
} }

View File

@@ -1,216 +0,0 @@
/*
* Copyright (C) 2006 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.database.IContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ServiceManager;
import android.os.Bundle;
import android.util.Config;
import android.util.Log;
/**
* {@hide}
*/
abstract class ContentServiceNative extends Binder implements IContentService
{
public ContentServiceNative()
{
attachInterface(this, descriptor);
}
/**
* Cast a Binder object into a content resolver interface, generating
* a proxy if needed.
*/
static public IContentService asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
IContentService in =
(IContentService)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ContentServiceProxy(obj);
}
/**
* Retrieve the system's default/global content service.
*/
static public IContentService getDefault()
{
if (gDefault != null) {
return gDefault;
}
IBinder b = ServiceManager.getService("content");
if (Config.LOGV) Log.v("ContentService", "default service binder = " + b);
gDefault = asInterface(b);
if (Config.LOGV) Log.v("ContentService", "default service = " + gDefault);
return gDefault;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
{
try {
switch (code) {
case 5038: {
data.readString(); // ignore the interface token that service generated
Uri uri = Uri.parse(data.readString());
notifyChange(uri, null, false, false);
return true;
}
case REGISTER_CONTENT_OBSERVER_TRANSACTION: {
Uri uri = Uri.CREATOR.createFromParcel(data);
boolean notifyForDescendents = data.readInt() != 0;
IContentObserver observer = IContentObserver.Stub.asInterface(data.readStrongBinder());
registerContentObserver(uri, notifyForDescendents, observer);
return true;
}
case UNREGISTER_CHANGE_OBSERVER_TRANSACTION: {
IContentObserver observer = IContentObserver.Stub.asInterface(data.readStrongBinder());
unregisterContentObserver(observer);
return true;
}
case NOTIFY_CHANGE_TRANSACTION: {
Uri uri = Uri.CREATOR.createFromParcel(data);
IContentObserver observer = IContentObserver.Stub.asInterface(data.readStrongBinder());
boolean observerWantsSelfNotifications = data.readInt() != 0;
boolean syncToNetwork = data.readInt() != 0;
notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork);
return true;
}
case START_SYNC_TRANSACTION: {
Uri url = null;
int hasUrl = data.readInt();
if (hasUrl != 0) {
url = Uri.CREATOR.createFromParcel(data);
}
startSync(url, data.readBundle());
return true;
}
case CANCEL_SYNC_TRANSACTION: {
Uri url = null;
int hasUrl = data.readInt();
if (hasUrl != 0) {
url = Uri.CREATOR.createFromParcel(data);
}
cancelSync(url);
return true;
}
default:
return super.onTransact(code, data, reply, flags);
}
} catch (Exception e) {
Log.e("ContentServiceNative", "Caught exception in transact", e);
}
return false;
}
public IBinder asBinder()
{
return this;
}
private static IContentService gDefault;
}
final class ContentServiceProxy implements IContentService
{
public ContentServiceProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public void registerContentObserver(Uri uri, boolean notifyForDescendents,
IContentObserver observer) throws RemoteException
{
Parcel data = Parcel.obtain();
uri.writeToParcel(data, 0);
data.writeInt(notifyForDescendents ? 1 : 0);
data.writeStrongInterface(observer);
mRemote.transact(REGISTER_CONTENT_OBSERVER_TRANSACTION, data, null, 0);
data.recycle();
}
public void unregisterContentObserver(IContentObserver observer) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeStrongInterface(observer);
mRemote.transact(UNREGISTER_CHANGE_OBSERVER_TRANSACTION, data, null, 0);
data.recycle();
}
public void notifyChange(Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, boolean syncToNetwork)
throws RemoteException {
Parcel data = Parcel.obtain();
uri.writeToParcel(data, 0);
data.writeStrongInterface(observer);
data.writeInt(observerWantsSelfNotifications ? 1 : 0);
data.writeInt(syncToNetwork ? 1 : 0);
mRemote.transact(NOTIFY_CHANGE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
public void startSync(Uri url, Bundle extras) throws RemoteException {
Parcel data = Parcel.obtain();
if (url == null) {
data.writeInt(0);
} else {
data.writeInt(1);
url.writeToParcel(data, 0);
}
extras.writeToParcel(data, 0);
mRemote.transact(START_SYNC_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
public void cancelSync(Uri url) throws RemoteException {
Parcel data = Parcel.obtain();
if (url == null) {
data.writeInt(0);
} else {
data.writeInt(1);
url.writeToParcel(data, 0);
}
mRemote.transact(CANCEL_SYNC_TRANSACTION, data, null /* reply */, IBinder.FLAG_ONEWAY);
data.recycle();
}
private IBinder mRemote;
}

View File

@@ -0,0 +1,83 @@
/*
* 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.ActiveSyncInfo;
import android.content.ISyncStatusObserver;
import android.content.SyncStatusInfo;
import android.net.Uri;
import android.os.Bundle;
import android.database.IContentObserver;
/**
* @hide
*/
interface IContentService {
void registerContentObserver(in Uri uri, boolean notifyForDescendentsn,
IContentObserver observer);
void unregisterContentObserver(IContentObserver observer);
void notifyChange(in Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, boolean syncToNetwork);
void startSync(in Uri url, in Bundle extras);
void cancelSync(in Uri uri);
/**
* Check if the provider should be synced when a network tickle is received
* @param providerName the provider whose setting we are querying
* @return true of the provider should be synced when a network tickle is received
*/
boolean getSyncProviderAutomatically(String providerName);
/**
* Set whether or not the provider is synced when it receives a network tickle.
*
* @param providerName the provider whose behavior is being controlled
* @param sync true if the provider should be synced when tickles are received for it
*/
void setSyncProviderAutomatically(String providerName, boolean sync);
void setListenForNetworkTickles(boolean flag);
boolean getListenForNetworkTickles();
/**
* Returns true if there is currently a sync operation for the given
* account or authority in the pending list, or actively being processed.
*/
boolean isSyncActive(String account, String authority);
ActiveSyncInfo getActiveSync();
/**
* Returns the status that matches the authority. If there are multiples accounts for
* the authority, the one with the latest "lastSuccessTime" status is returned.
* @param authority the authority whose row should be selected
* @return the SyncStatusInfo for the authority, or null if none exists
*/
SyncStatusInfo getStatusByAuthority(String authority);
/**
* Return true if the pending status is true of any matching authorities.
*/
boolean isAuthorityPending(String account, String authority);
void addStatusChangeListener(int mask, ISyncStatusObserver callback);
void removeStatusChangeListener(ISyncStatusObserver callback);
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright (C) 2006 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.database.IContentObserver;
import android.net.Uri;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Bundle;
/**
* {@hide}
*/
public interface IContentService extends IInterface
{
public void registerContentObserver(Uri uri, boolean notifyForDescendentsn,
IContentObserver observer) throws RemoteException;
public void unregisterContentObserver(IContentObserver observer) throws RemoteException;
public void notifyChange(Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, boolean syncToNetwork)
throws RemoteException;
public void startSync(Uri url, Bundle extras) throws RemoteException;
public void cancelSync(Uri uri) throws RemoteException;
static final String SERVICE_NAME = "content";
/* IPC constants */
static final String descriptor = "android.content.IContentService";
static final int REGISTER_CONTENT_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
static final int UNREGISTER_CHANGE_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
static final int NOTIFY_CHANGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
static final int START_SYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4;
static final int CANCEL_SYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
}

View File

@@ -0,0 +1,24 @@
/*
* 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;
/**
* @hide
*/
oneway interface ISyncStatusObserver {
void onStatusChanged(int which);
}

View File

@@ -32,8 +32,6 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.Uri; import android.net.Uri;
@@ -43,18 +41,13 @@ import android.os.HandlerThread;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.Parcel;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.Process; import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.provider.Sync;
import android.provider.Settings; import android.provider.Settings;
import android.provider.Sync.History;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.text.format.Time; import android.text.format.Time;
@@ -73,13 +66,12 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.PriorityQueue; import java.util.PriorityQueue;
import java.util.Random; import java.util.Random;
import java.util.Observer;
import java.util.Observable;
/** /**
* @hide * @hide
@@ -138,7 +130,6 @@ class SyncManager {
volatile private PowerManager.WakeLock mHandleAlarmWakeLock; volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
volatile private boolean mDataConnectionIsConnected = false; volatile private boolean mDataConnectionIsConnected = false;
volatile private boolean mStorageIsLow = false; volatile private boolean mStorageIsLow = false;
private Sync.Settings.QueryMap mSyncSettings;
private final NotificationManager mNotificationMgr; private final NotificationManager mNotificationMgr;
private AlarmManager mAlarmService = null; private AlarmManager mAlarmService = null;
@@ -225,17 +216,6 @@ class SyncManager {
private static final String SYNC_POLL_ALARM = "android.content.syncmanager.SYNC_POLL_ALARM"; private static final String SYNC_POLL_ALARM = "android.content.syncmanager.SYNC_POLL_ALARM";
private final SyncHandler mSyncHandler; private final SyncHandler mSyncHandler;
private static final String[] SYNC_ACTIVE_PROJECTION = new String[]{
Sync.Active.ACCOUNT,
Sync.Active.AUTHORITY,
Sync.Active.START_TIME,
};
private static final String[] SYNC_PENDING_PROJECTION = new String[]{
Sync.Pending.ACCOUNT,
Sync.Pending.AUTHORITY
};
private static final int MAX_SYNC_POLL_DELAY_SECONDS = 36 * 60 * 60; // 36 hours private static final int MAX_SYNC_POLL_DELAY_SECONDS = 36 * 60 * 60; // 36 hours
private static final int MIN_SYNC_POLL_DELAY_SECONDS = 24 * 60 * 60; // 24 hours private static final int MIN_SYNC_POLL_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
@@ -289,6 +269,14 @@ class SyncManager {
HANDLE_SYNC_ALARM_WAKE_LOCK); HANDLE_SYNC_ALARM_WAKE_LOCK);
mHandleAlarmWakeLock.setReferenceCounted(false); mHandleAlarmWakeLock.setReferenceCounted(false);
mSyncStorageEngine.addStatusChangeListener(
SyncStorageEngine.CHANGE_SETTINGS, new ISyncStatusObserver.Stub() {
public void onStatusChanged(int which) {
// force the sync loop to run if the settings change
sendCheckAlarmsMessage();
}
});
if (!factoryTest) { if (!factoryTest) {
AccountMonitorListener listener = new AccountMonitorListener() { AccountMonitorListener listener = new AccountMonitorListener() {
public void onAccountsUpdated(String[] accounts) { public void onAccountsUpdated(String[] accounts) {
@@ -448,20 +436,10 @@ class SyncManager {
return mActiveSyncContext; return mActiveSyncContext;
} }
private Sync.Settings.QueryMap getSyncSettings() { public SyncStorageEngine getSyncStorageEngine() {
if (mSyncSettings == null) { return mSyncStorageEngine;
mSyncSettings = new Sync.Settings.QueryMap(mContext.getContentResolver(), true,
new Handler());
mSyncSettings.addObserver(new Observer(){
public void update(Observable o, Object arg) {
// force the sync loop to run if the settings change
sendCheckAlarmsMessage();
}
});
}
return mSyncSettings;
} }
private void ensureContentResolver() { private void ensureContentResolver() {
if (mContentResolver == null) { if (mContentResolver == null) {
mContentResolver = mContext.getContentResolver(); mContentResolver = mContext.getContentResolver();
@@ -574,15 +552,15 @@ class SyncManager {
int source; int source;
if (uploadOnly) { if (uploadOnly) {
source = Sync.History.SOURCE_LOCAL; source = SyncStorageEngine.SOURCE_LOCAL;
} else if (force) { } else if (force) {
source = Sync.History.SOURCE_USER; source = SyncStorageEngine.SOURCE_USER;
} else if (url == null) { } else if (url == null) {
source = Sync.History.SOURCE_POLL; source = SyncStorageEngine.SOURCE_POLL;
} else { } else {
// this isn't strictly server, since arbitrary callers can (and do) request // this isn't strictly server, since arbitrary callers can (and do) request
// a non-forced two-way sync on a specific url // a non-forced two-way sync on a specific url
source = Sync.History.SOURCE_SERVER; source = SyncStorageEngine.SOURCE_SERVER;
} }
List<String> names = new ArrayList<String>(); List<String> names = new ArrayList<String>();
@@ -667,9 +645,7 @@ class SyncManager {
public void updateHeartbeatTime() { public void updateHeartbeatTime() {
mHeartbeatTime = SystemClock.elapsedRealtime(); mHeartbeatTime = SystemClock.elapsedRealtime();
ensureContentResolver(); mSyncStorageEngine.reportActiveChange();
mContentResolver.notifyChange(Sync.Active.CONTENT_URI,
null /* this change wasn't made through an observer */);
} }
private void sendSyncAlarmMessage() { private void sendSyncAlarmMessage() {
@@ -876,7 +852,7 @@ class SyncManager {
final String key; final String key;
long earliestRunTime; long earliestRunTime;
long delay; long delay;
Long rowId = null; SyncStorageEngine.PendingOperation pendingOperation;
SyncOperation(String account, int source, String authority, Bundle extras, long delay) { SyncOperation(String account, int source, String authority, Bundle extras, long delay) {
this.account = account; this.account = account;
@@ -916,7 +892,7 @@ class SyncManager {
sb.append(" when: ").append(earliestRunTime); sb.append(" when: ").append(earliestRunTime);
sb.append(" delay: ").append(delay); sb.append(" delay: ").append(delay);
sb.append(" key: {").append(key).append("}"); sb.append(" key: {").append(key).append("}");
if (rowId != null) sb.append(" rowId: ").append(rowId); if (pendingOperation != null) sb.append(" pendingOperation: ").append(pendingOperation);
return sb.toString(); return sb.toString();
} }
@@ -999,242 +975,262 @@ class SyncManager {
protected void dump(FileDescriptor fd, PrintWriter pw) { protected void dump(FileDescriptor fd, PrintWriter pw) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
dumpSyncState(sb); dumpSyncState(pw, sb);
sb.append("\n");
if (isSyncEnabled()) { if (isSyncEnabled()) {
dumpSyncHistory(sb); dumpSyncHistory(pw, sb);
} }
pw.println(sb.toString());
} }
protected void dumpSyncState(StringBuilder sb) { static String formatTime(long time) {
sb.append("sync enabled: ").append(isSyncEnabled()).append("\n"); Time tobj = new Time();
sb.append("data connected: ").append(mDataConnectionIsConnected).append("\n"); tobj.set(time);
sb.append("memory low: ").append(mStorageIsLow).append("\n"); return tobj.format("%Y-%m-%d %H:%M:%S");
}
protected void dumpSyncState(PrintWriter pw, StringBuilder sb) {
pw.print("sync enabled: "); pw.println(isSyncEnabled());
pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
pw.print("memory low: "); pw.println(mStorageIsLow);
final String[] accounts = mAccounts; final String[] accounts = mAccounts;
sb.append("accounts: "); pw.print("accounts: ");
if (accounts != null) { if (accounts != null) {
sb.append(accounts.length); pw.println(accounts.length);
} else { } else {
sb.append("none"); pw.println("none");
} }
sb.append("\n");
final long now = SystemClock.elapsedRealtime(); final long now = SystemClock.elapsedRealtime();
sb.append("now: ").append(now).append("\n"); pw.print("now: "); pw.println(now);
sb.append("uptime: ").append(DateUtils.formatElapsedTime(now/1000)).append(" (HH:MM:SS)\n"); pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
sb.append("time spent syncing : ") pw.println(" (HH:MM:SS)");
.append(DateUtils.formatElapsedTime( pw.print("time spent syncing: ");
mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000)) pw.print(DateUtils.formatElapsedTime(
.append(" (HH:MM:SS), sync ") mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
.append(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ") pw.print(" (HH:MM:SS), sync ");
.append("in progress").append("\n"); pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
pw.println("in progress");
if (mSyncHandler.mAlarmScheduleTime != null) { if (mSyncHandler.mAlarmScheduleTime != null) {
sb.append("next alarm time: ").append(mSyncHandler.mAlarmScheduleTime) pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime);
.append(" (") pw.print(" (");
.append(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000)) pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000));
.append(" (HH:MM:SS) from now)\n"); pw.println(" (HH:MM:SS) from now)");
} else { } else {
sb.append("no alarm is scheduled (there had better not be any pending syncs)\n"); pw.println("no alarm is scheduled (there had better not be any pending syncs)");
} }
sb.append("active sync: ").append(mActiveSyncContext).append("\n"); pw.print("active sync: "); pw.println(mActiveSyncContext);
sb.append("notification info: "); pw.print("notification info: ");
sb.setLength(0);
mSyncHandler.mSyncNotificationInfo.toString(sb); mSyncHandler.mSyncNotificationInfo.toString(sb);
sb.append("\n"); pw.println(sb.toString());
synchronized (mSyncQueue) { synchronized (mSyncQueue) {
sb.append("sync queue: "); pw.print("sync queue: ");
sb.setLength(0);
mSyncQueue.dump(sb); mSyncQueue.dump(sb);
pw.println(sb.toString());
} }
Cursor c = mSyncStorageEngine.query(Sync.Active.CONTENT_URI, ActiveSyncInfo active = mSyncStorageEngine.getActiveSync();
SYNC_ACTIVE_PROJECTION, null, null, null); if (active != null) {
sb.append("\n"); SyncStorageEngine.AuthorityInfo authority
try { = mSyncStorageEngine.getAuthority(active.authorityId);
if (c.moveToNext()) { final long durationInSeconds = (now - active.startTime) / 1000;
final long durationInSeconds = (now - c.getLong(2)) / 1000; pw.print("Active sync: ");
sb.append("Active sync: ").append(c.getString(0)) pw.print(authority != null ? authority.account : "<no account>");
.append(" ").append(c.getString(1)) pw.print(" ");
.append(", duration is ") pw.print(authority != null ? authority.authority : "<no account>");
.append(DateUtils.formatElapsedTime(durationInSeconds)).append(".\n"); pw.print(", duration is ");
} else { pw.println(DateUtils.formatElapsedTime(durationInSeconds));
sb.append("No sync is in progress.\n");
}
} finally {
c.close();
}
c = mSyncStorageEngine.query(Sync.Pending.CONTENT_URI,
SYNC_PENDING_PROJECTION, null, null, "account, authority");
sb.append("\nPending Syncs\n");
try {
if (c.getCount() != 0) {
dumpSyncPendingHeader(sb);
while (c.moveToNext()) {
dumpSyncPendingRow(sb, c);
}
dumpSyncPendingFooter(sb);
} else {
sb.append("none\n");
}
} finally {
c.close();
}
String currentAccount = null;
c = mSyncStorageEngine.query(Sync.Status.CONTENT_URI,
STATUS_PROJECTION, null, null, "account, authority");
sb.append("\nSync history by account and authority\n");
try {
while (c.moveToNext()) {
if (!TextUtils.equals(currentAccount, c.getString(0))) {
if (currentAccount != null) {
dumpSyncHistoryFooter(sb);
}
currentAccount = c.getString(0);
dumpSyncHistoryHeader(sb, currentAccount);
}
dumpSyncHistoryRow(sb, c);
}
if (c.getCount() > 0) dumpSyncHistoryFooter(sb);
} finally {
c.close();
}
}
private void dumpSyncHistoryHeader(StringBuilder sb, String account) {
sb.append(" Account: ").append(account).append("\n");
sb.append(" ___________________________________________________________________________________________________________________________\n");
sb.append(" | | num times synced | total | last success | |\n");
sb.append(" | authority | local | poll | server | user | total | duration | source | time | result if failing |\n");
}
private static String[] STATUS_PROJECTION = new String[]{
Sync.Status.ACCOUNT, // 0
Sync.Status.AUTHORITY, // 1
Sync.Status.NUM_SYNCS, // 2
Sync.Status.TOTAL_ELAPSED_TIME, // 3
Sync.Status.NUM_SOURCE_LOCAL, // 4
Sync.Status.NUM_SOURCE_POLL, // 5
Sync.Status.NUM_SOURCE_SERVER, // 6
Sync.Status.NUM_SOURCE_USER, // 7
Sync.Status.LAST_SUCCESS_SOURCE, // 8
Sync.Status.LAST_SUCCESS_TIME, // 9
Sync.Status.LAST_FAILURE_SOURCE, // 10
Sync.Status.LAST_FAILURE_TIME, // 11
Sync.Status.LAST_FAILURE_MESG // 12
};
private void dumpSyncHistoryRow(StringBuilder sb, Cursor c) {
boolean hasSuccess = !c.isNull(9);
boolean hasFailure = !c.isNull(11);
Time timeSuccess = new Time();
if (hasSuccess) timeSuccess.set(c.getLong(9));
Time timeFailure = new Time();
if (hasFailure) timeFailure.set(c.getLong(11));
sb.append(String.format(" | %-15s | %5d | %5d | %6d | %5d | %5d | %8s | %7s | %19s | %19s |\n",
c.getString(1),
c.getLong(4),
c.getLong(5),
c.getLong(6),
c.getLong(7),
c.getLong(2),
DateUtils.formatElapsedTime(c.getLong(3)/1000),
hasSuccess ? Sync.History.SOURCES[c.getInt(8)] : "",
hasSuccess ? timeSuccess.format("%Y-%m-%d %H:%M:%S") : "",
hasFailure ? History.mesgToString(c.getString(12)) : ""));
}
private void dumpSyncHistoryFooter(StringBuilder sb) {
sb.append(" |___________________________________________________________________________________________________________________________|\n");
}
private void dumpSyncPendingHeader(StringBuilder sb) {
sb.append(" ____________________________________________________\n");
sb.append(" | account | authority |\n");
}
private void dumpSyncPendingRow(StringBuilder sb, Cursor c) {
sb.append(String.format(" | %-30s | %-15s |\n", c.getString(0), c.getString(1)));
}
private void dumpSyncPendingFooter(StringBuilder sb) {
sb.append(" |__________________________________________________|\n");
}
protected void dumpSyncHistory(StringBuilder sb) {
Cursor c = mSyncStorageEngine.query(Sync.History.CONTENT_URI, null, "event=?",
new String[]{String.valueOf(Sync.History.EVENT_STOP)},
Sync.HistoryColumns.EVENT_TIME + " desc");
try {
long numSyncsLastHour = 0, durationLastHour = 0;
long numSyncsLastDay = 0, durationLastDay = 0;
long numSyncsLastWeek = 0, durationLastWeek = 0;
long numSyncsLast4Weeks = 0, durationLast4Weeks = 0;
long numSyncsTotal = 0, durationTotal = 0;
long now = System.currentTimeMillis();
int indexEventTime = c.getColumnIndexOrThrow(Sync.History.EVENT_TIME);
int indexElapsedTime = c.getColumnIndexOrThrow(Sync.History.ELAPSED_TIME);
while (c.moveToNext()) {
long duration = c.getLong(indexElapsedTime);
long endTime = c.getLong(indexEventTime) + duration;
long millisSinceStart = now - endTime;
numSyncsTotal++;
durationTotal += duration;
if (millisSinceStart < MILLIS_IN_HOUR) {
numSyncsLastHour++;
durationLastHour += duration;
}
if (millisSinceStart < MILLIS_IN_DAY) {
numSyncsLastDay++;
durationLastDay += duration;
}
if (millisSinceStart < MILLIS_IN_WEEK) {
numSyncsLastWeek++;
durationLastWeek += duration;
}
if (millisSinceStart < MILLIS_IN_4WEEKS) {
numSyncsLast4Weeks++;
durationLast4Weeks += duration;
}
}
dumpSyncIntervalHeader(sb);
dumpSyncInterval(sb, "hour", MILLIS_IN_HOUR, numSyncsLastHour, durationLastHour);
dumpSyncInterval(sb, "day", MILLIS_IN_DAY, numSyncsLastDay, durationLastDay);
dumpSyncInterval(sb, "week", MILLIS_IN_WEEK, numSyncsLastWeek, durationLastWeek);
dumpSyncInterval(sb, "4 weeks",
MILLIS_IN_4WEEKS, numSyncsLast4Weeks, durationLast4Weeks);
dumpSyncInterval(sb, "total", 0, numSyncsTotal, durationTotal);
dumpSyncIntervalFooter(sb);
} finally {
c.close();
}
}
private void dumpSyncIntervalHeader(StringBuilder sb) {
sb.append("Sync Stats\n");
sb.append(" ___________________________________________________________\n");
sb.append(" | | | duration in sec | |\n");
sb.append(" | interval | count | average | total | % of interval |\n");
}
private void dumpSyncInterval(StringBuilder sb, String label,
long interval, long numSyncs, long duration) {
sb.append(String.format(" | %-8s | %6d | %8.1f | %8.1f",
label, numSyncs, ((float)duration/numSyncs)/1000, (float)duration/1000));
if (interval > 0) {
sb.append(String.format(" | %13.2f |\n", ((float)duration/interval)*100.0));
} else { } else {
sb.append(String.format(" | %13s |\n", "na")); pw.println("No sync is in progress.");
}
ArrayList<SyncStorageEngine.PendingOperation> ops
= mSyncStorageEngine.getPendingOperations();
if (ops != null && ops.size() > 0) {
pw.println();
pw.println("Pending Syncs");
final int N = ops.size();
for (int i=0; i<N; i++) {
SyncStorageEngine.PendingOperation op = ops.get(i);
pw.print(" #"); pw.print(i); pw.print(": account=");
pw.print(op.account); pw.print(" authority=");
pw.println(op.authority);
if (op.extras != null && op.extras.size() > 0) {
sb.setLength(0);
SyncOperation.extrasToStringBuilder(op.extras, sb);
pw.print(" extras: "); pw.println(sb.toString());
}
}
}
HashSet<String> processedAccounts = new HashSet<String>();
ArrayList<SyncStatusInfo> statuses
= mSyncStorageEngine.getSyncStatus();
if (statuses != null && statuses.size() > 0) {
pw.println();
pw.println("Sync Status");
final int N = statuses.size();
for (int i=0; i<N; i++) {
SyncStatusInfo status = statuses.get(i);
SyncStorageEngine.AuthorityInfo authority
= mSyncStorageEngine.getAuthority(status.authorityId);
if (authority != null) {
String curAccount = authority.account;
if (processedAccounts.contains(curAccount)) {
continue;
}
processedAccounts.add(curAccount);
pw.print(" Account "); pw.print(authority.account);
pw.println(":");
for (int j=i; j<N; j++) {
status = statuses.get(j);
authority = mSyncStorageEngine.getAuthority(status.authorityId);
if (!curAccount.equals(authority.account)) {
continue;
}
pw.print(" "); pw.print(authority.authority);
pw.println(":");
pw.print(" count: local="); pw.print(status.numSourceLocal);
pw.print(" poll="); pw.print(status.numSourcePoll);
pw.print(" server="); pw.print(status.numSourceServer);
pw.print(" user="); pw.print(status.numSourceUser);
pw.print(" total="); pw.println(status.numSyncs);
pw.print(" total duration: ");
pw.println(DateUtils.formatElapsedTime(
status.totalElapsedTime/1000));
if (status.lastSuccessTime != 0) {
pw.print(" SUCCESS: source=");
pw.print(SyncStorageEngine.SOURCES[
status.lastSuccessSource]);
pw.print(" time=");
pw.println(formatTime(status.lastSuccessTime));
} else {
pw.print(" FAILURE: source=");
pw.print(SyncStorageEngine.SOURCES[
status.lastFailureSource]);
pw.print(" initialTime=");
pw.print(formatTime(status.initialFailureTime));
pw.print(" lastTime=");
pw.println(formatTime(status.lastFailureTime));
pw.print(" message: "); pw.println(status.lastFailureMesg);
}
}
}
}
} }
} }
private void dumpSyncIntervalFooter(StringBuilder sb) { private void dumpTimeSec(PrintWriter pw, long time) {
sb.append(" |_________________________________________________________|\n"); pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
pw.print('s');
}
private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) {
pw.print("Success ("); pw.print(ds.successCount);
if (ds.successCount > 0) {
pw.print(" for "); dumpTimeSec(pw, ds.successTime);
pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount);
}
pw.print(") Failure ("); pw.print(ds.failureCount);
if (ds.failureCount > 0) {
pw.print(" for "); dumpTimeSec(pw, ds.failureTime);
pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount);
}
pw.println(")");
}
protected void dumpSyncHistory(PrintWriter pw, StringBuilder sb) {
SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics();
if (dses != null && dses[0] != null) {
pw.println();
pw.println("Sync Statistics");
pw.print(" Today: "); dumpDayStatistic(pw, dses[0]);
int today = dses[0].day;
int i;
SyncStorageEngine.DayStats ds;
// Print each day in the current week.
for (i=1; i<=6 && i < dses.length; i++) {
ds = dses[i];
if (ds == null) break;
int delta = today-ds.day;
if (delta > 6) break;
pw.print(" Day-"); pw.print(delta); pw.print(": ");
dumpDayStatistic(pw, ds);
}
// Aggregate all following days into weeks and print totals.
int weekDay = today;
while (i < dses.length) {
SyncStorageEngine.DayStats aggr = null;
weekDay -= 7;
while (i < dses.length) {
ds = dses[i];
if (ds == null) {
i = dses.length;
break;
}
int delta = weekDay-ds.day;
if (delta > 6) break;
i++;
if (aggr == null) {
aggr = new SyncStorageEngine.DayStats(weekDay);
}
aggr.successCount += ds.successCount;
aggr.successTime += ds.successTime;
aggr.failureCount += ds.failureCount;
aggr.failureTime += ds.failureTime;
}
if (aggr != null) {
pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": ");
dumpDayStatistic(pw, aggr);
}
}
}
ArrayList<SyncStorageEngine.SyncHistoryItem> items
= mSyncStorageEngine.getSyncHistory();
if (items != null && items.size() > 0) {
pw.println();
pw.println("Recent Sync History");
final int N = items.size();
for (int i=0; i<N; i++) {
SyncStorageEngine.SyncHistoryItem item = items.get(i);
SyncStorageEngine.AuthorityInfo authority
= mSyncStorageEngine.getAuthority(item.authorityId);
pw.print(" #"); pw.print(i+1); pw.print(": ");
pw.print(authority != null ? authority.account : "<no account>");
pw.print(" ");
pw.print(authority != null ? authority.authority : "<no account>");
Time time = new Time();
time.set(item.eventTime);
pw.print(" "); pw.print(SyncStorageEngine.SOURCES[item.source]);
pw.print(" @ ");
pw.print(formatTime(item.eventTime));
pw.print(" for ");
dumpTimeSec(pw, item.elapsedTime);
pw.println();
if (item.event != SyncStorageEngine.EVENT_STOP
|| item.upstreamActivity !=0
|| item.downstreamActivity != 0) {
pw.print(" event="); pw.print(item.event);
pw.print(" upstreamActivity="); pw.print(item.upstreamActivity);
pw.print(" downstreamActivity="); pw.println(item.downstreamActivity);
}
if (item.mesg != null
&& !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) {
pw.print(" mesg="); pw.println(item.mesg);
}
}
}
} }
/** /**
@@ -1461,7 +1457,6 @@ class SyncManager {
// found that is runnable (not disabled, etc). If that one is ready to run then // found that is runnable (not disabled, etc). If that one is ready to run then
// start it, otherwise just get out. // start it, otherwise just get out.
SyncOperation syncOperation; SyncOperation syncOperation;
final Sync.Settings.QueryMap syncSettings = getSyncSettings();
final ConnectivityManager connManager = (ConnectivityManager) final ConnectivityManager connManager = (ConnectivityManager)
mContext.getSystemService(Context.CONNECTIVITY_SERVICE); mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
final boolean backgroundDataSetting = connManager.getBackgroundDataSetting(); final boolean backgroundDataSetting = connManager.getBackgroundDataSetting();
@@ -1488,9 +1483,9 @@ class SyncManager {
final boolean force = syncOperation.extras.getBoolean( final boolean force = syncOperation.extras.getBoolean(
ContentResolver.SYNC_EXTRAS_FORCE, false); ContentResolver.SYNC_EXTRAS_FORCE, false);
if (!force && (!backgroundDataSetting if (!force && (!backgroundDataSetting
|| !syncSettings.getListenForNetworkTickles() || !mSyncStorageEngine.getListenForNetworkTickles()
|| !syncSettings.getSyncProviderAutomatically( || !mSyncStorageEngine.getSyncProviderAutomatically(
syncOperation.authority))) { null, syncOperation.authority))) {
if (isLoggable) { if (isLoggable) {
Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation); Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation);
} }
@@ -1616,7 +1611,7 @@ class SyncManager {
if (isLoggable) { if (isLoggable) {
Log.v(TAG, "finished sync operation " + syncOperation); Log.v(TAG, "finished sync operation " + syncOperation);
} }
historyMessage = History.MESG_SUCCESS; historyMessage = SyncStorageEngine.MESG_SUCCESS;
// TODO: set these correctly when the SyncResult is extended to include it // TODO: set these correctly when the SyncResult is extended to include it
downstreamActivity = 0; downstreamActivity = 0;
upstreamActivity = 0; upstreamActivity = 0;
@@ -1640,7 +1635,7 @@ class SyncManager {
} catch (RemoteException e) { } catch (RemoteException e) {
// we don't need to retry this in this case // we don't need to retry this in this case
} }
historyMessage = History.MESG_CANCELED; historyMessage = SyncStorageEngine.MESG_CANCELED;
downstreamActivity = 0; downstreamActivity = 0;
upstreamActivity = 0; upstreamActivity = 0;
} }
@@ -1675,14 +1670,22 @@ class SyncManager {
* If SyncResult.error() is true then it is safe to call this. * If SyncResult.error() is true then it is safe to call this.
*/ */
private int syncResultToErrorNumber(SyncResult syncResult) { private int syncResultToErrorNumber(SyncResult syncResult) {
if (syncResult.syncAlreadyInProgress) return History.ERROR_SYNC_ALREADY_IN_PROGRESS; if (syncResult.syncAlreadyInProgress)
if (syncResult.stats.numAuthExceptions > 0) return History.ERROR_AUTHENTICATION; return SyncStorageEngine.ERROR_SYNC_ALREADY_IN_PROGRESS;
if (syncResult.stats.numIoExceptions > 0) return History.ERROR_IO; if (syncResult.stats.numAuthExceptions > 0)
if (syncResult.stats.numParseExceptions > 0) return History.ERROR_PARSE; return SyncStorageEngine.ERROR_AUTHENTICATION;
if (syncResult.stats.numConflictDetectedExceptions > 0) return History.ERROR_CONFLICT; if (syncResult.stats.numIoExceptions > 0)
if (syncResult.tooManyDeletions) return History.ERROR_TOO_MANY_DELETIONS; return SyncStorageEngine.ERROR_IO;
if (syncResult.tooManyRetries) return History.ERROR_TOO_MANY_RETRIES; if (syncResult.stats.numParseExceptions > 0)
if (syncResult.databaseError) return History.ERROR_INTERNAL; return SyncStorageEngine.ERROR_PARSE;
if (syncResult.stats.numConflictDetectedExceptions > 0)
return SyncStorageEngine.ERROR_CONFLICT;
if (syncResult.tooManyDeletions)
return SyncStorageEngine.ERROR_TOO_MANY_DELETIONS;
if (syncResult.tooManyRetries)
return SyncStorageEngine.ERROR_TOO_MANY_RETRIES;
if (syncResult.databaseError)
return SyncStorageEngine.ERROR_INTERNAL;
throw new IllegalStateException("we are not in an error state, " + syncResult); throw new IllegalStateException("we are not in an error state, " + syncResult);
} }
@@ -1904,7 +1907,8 @@ class SyncManager {
final int source = syncOperation.syncSource; final int source = syncOperation.syncSource;
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
EventLog.writeEvent(2720, syncOperation.authority, Sync.History.EVENT_START, source); EventLog.writeEvent(2720, syncOperation.authority,
SyncStorageEngine.EVENT_START, source);
return mSyncStorageEngine.insertStartSyncEvent( return mSyncStorageEngine.insertStartSyncEvent(
syncOperation.account, syncOperation.authority, now, source); syncOperation.account, syncOperation.authority, now, source);
@@ -1912,7 +1916,8 @@ class SyncManager {
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
int upstreamActivity, int downstreamActivity, long elapsedTime) { int upstreamActivity, int downstreamActivity, long elapsedTime) {
EventLog.writeEvent(2720, syncOperation.authority, Sync.History.EVENT_STOP, syncOperation.syncSource); EventLog.writeEvent(2720, syncOperation.authority,
SyncStorageEngine.EVENT_STOP, syncOperation.syncSource);
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, resultMessage, mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, resultMessage,
downstreamActivity, upstreamActivity); downstreamActivity, upstreamActivity);
@@ -1921,18 +1926,6 @@ class SyncManager {
static class SyncQueue { static class SyncQueue {
private SyncStorageEngine mSyncStorageEngine; private SyncStorageEngine mSyncStorageEngine;
private final String[] COLUMNS = new String[]{
"_id",
"authority",
"account",
"extras",
"source"
};
private static final int COLUMN_ID = 0;
private static final int COLUMN_AUTHORITY = 1;
private static final int COLUMN_ACCOUNT = 2;
private static final int COLUMN_EXTRAS = 3;
private static final int COLUMN_SOURCE = 4;
private static final boolean DEBUG_CHECK_DATA_CONSISTENCY = false; private static final boolean DEBUG_CHECK_DATA_CONSISTENCY = false;
@@ -1946,25 +1939,28 @@ class SyncManager {
public SyncQueue(SyncStorageEngine syncStorageEngine) { public SyncQueue(SyncStorageEngine syncStorageEngine) {
mSyncStorageEngine = syncStorageEngine; mSyncStorageEngine = syncStorageEngine;
Cursor cursor = mSyncStorageEngine.getPendingSyncsCursor(COLUMNS); ArrayList<SyncStorageEngine.PendingOperation> ops
try { = mSyncStorageEngine.getPendingOperations();
while (cursor.moveToNext()) { final int N = ops.size();
add(cursorToOperation(cursor), for (int i=0; i<N; i++) {
true /* this is being added from the database */); SyncStorageEngine.PendingOperation op = ops.get(i);
} SyncOperation syncOperation = new SyncOperation(
} finally { op.account, op.syncSource, op.authority, op.extras, 0);
cursor.close(); syncOperation.pendingOperation = op;
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */); add(syncOperation, op);
} }
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */);
} }
public boolean add(SyncOperation operation) { public boolean add(SyncOperation operation) {
return add(new SyncOperation(operation), return add(new SyncOperation(operation),
false /* this is not coming from the database */); null /* this is not coming from the database */);
} }
private boolean add(SyncOperation operation, boolean fromDatabase) { private boolean add(SyncOperation operation,
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase); SyncStorageEngine.PendingOperation pop) {
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
// If this operation is expedited then set its earliestRunTime to be immediately // If this operation is expedited then set its earliestRunTime to be immediately
// before the head of the list, or not if none are in the list. // before the head of the list, or not if none are in the list.
@@ -1996,7 +1992,7 @@ class SyncManager {
if (existingOperation != null if (existingOperation != null
&& operation.earliestRunTime >= existingOperation.earliestRunTime) { && operation.earliestRunTime >= existingOperation.earliestRunTime) {
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase); if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
return false; return false;
} }
@@ -2004,26 +2000,17 @@ class SyncManager {
removeByKey(operationKey); removeByKey(operationKey);
} }
if (operation.rowId == null) { operation.pendingOperation = pop;
byte[] extrasData = null; if (operation.pendingOperation == null) {
Parcel parcel = Parcel.obtain(); pop = new SyncStorageEngine.PendingOperation(
try { operation.account, operation.syncSource,
operation.extras.writeToParcel(parcel, 0); operation.authority, operation.extras);
extrasData = parcel.marshall(); pop = mSyncStorageEngine.insertIntoPending(pop);
} finally { if (pop == null) {
parcel.recycle();
}
ContentValues values = new ContentValues();
values.put("account", operation.account);
values.put("authority", operation.authority);
values.put("source", operation.syncSource);
values.put("extras", extrasData);
Uri pendingUri = mSyncStorageEngine.insertIntoPending(values);
operation.rowId = pendingUri == null ? null : ContentUris.parseId(pendingUri);
if (operation.rowId == null) {
throw new IllegalStateException("error adding pending sync operation " throw new IllegalStateException("error adding pending sync operation "
+ operation); + operation);
} }
operation.pendingOperation = pop;
} }
if (DEBUG_CHECK_DATA_CONSISTENCY) { if (DEBUG_CHECK_DATA_CONSISTENCY) {
@@ -2033,7 +2020,7 @@ class SyncManager {
} }
mOpsByKey.put(operationKey, operation); mOpsByKey.put(operationKey, operation);
mOpsByWhen.add(operation); mOpsByWhen.add(operation);
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase); if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
return true; return true;
} }
@@ -2045,7 +2032,7 @@ class SyncManager {
"unable to find " + operationToRemove + " in mOpsByWhen"); "unable to find " + operationToRemove + " in mOpsByWhen");
} }
if (mSyncStorageEngine.deleteFromPending(operationToRemove.rowId) != 1) { if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) {
throw new IllegalStateException("unable to find pending row for " throw new IllegalStateException("unable to find pending row for "
+ operationToRemove); + operationToRemove);
} }
@@ -2065,7 +2052,7 @@ class SyncManager {
throw new IllegalStateException("unable to find " + operation + " in mOpsByKey"); throw new IllegalStateException("unable to find " + operation + " in mOpsByKey");
} }
if (mSyncStorageEngine.deleteFromPending(operation.rowId) != 1) { if (!mSyncStorageEngine.deleteFromPending(operation.pendingOperation)) {
throw new IllegalStateException("unable to find pending row for " + operation); throw new IllegalStateException("unable to find pending row for " + operation);
} }
@@ -2087,7 +2074,7 @@ class SyncManager {
"unable to find " + syncOperation + " in mOpsByWhen"); "unable to find " + syncOperation + " in mOpsByWhen");
} }
if (mSyncStorageEngine.deleteFromPending(syncOperation.rowId) != 1) { if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) {
throw new IllegalStateException("unable to find pending row for " throw new IllegalStateException("unable to find pending row for "
+ syncOperation); + syncOperation);
} }
@@ -2128,48 +2115,29 @@ class SyncManager {
} }
if (checkDatabase) { if (checkDatabase) {
// check that the DB contains the same rows as the in-memory data structures final int N = mSyncStorageEngine.getPendingOperationCount();
Cursor cursor = mSyncStorageEngine.getPendingSyncsCursor(COLUMNS); if (mOpsByKey.size() != N) {
try { ArrayList<SyncStorageEngine.PendingOperation> ops
if (mOpsByKey.size() != cursor.getCount()) { = mSyncStorageEngine.getPendingOperations();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
DatabaseUtils.dumpCursor(cursor, sb); for (int i=0; i<N; i++) {
SyncStorageEngine.PendingOperation op = ops.get(i);
sb.append("#");
sb.append(i);
sb.append(": account=");
sb.append(op.account);
sb.append(" syncSource=");
sb.append(op.syncSource);
sb.append(" authority=");
sb.append(op.authority);
sb.append("\n"); sb.append("\n");
dump(sb);
throw new IllegalStateException("DB size mismatch: "
+ mOpsByKey .size() + " != " + cursor.getCount() + "\n"
+ sb.toString());
} }
} finally { dump(sb);
cursor.close(); throw new IllegalStateException("DB size mismatch: "
+ mOpsByKey.size() + " != " + N + "\n"
+ sb.toString());
} }
} }
} }
private SyncOperation cursorToOperation(Cursor cursor) {
byte[] extrasData = cursor.getBlob(COLUMN_EXTRAS);
Bundle extras;
Parcel parcel = Parcel.obtain();
try {
parcel.unmarshall(extrasData, 0, extrasData.length);
parcel.setDataPosition(0);
extras = parcel.readBundle();
} catch (RuntimeException e) {
// A RuntimeException is thrown if we were unable to parse the parcel.
// Create an empty parcel in this case.
extras = new Bundle();
} finally {
parcel.recycle();
}
SyncOperation syncOperation = new SyncOperation(
cursor.getString(COLUMN_ACCOUNT),
cursor.getInt(COLUMN_SOURCE),
cursor.getString(COLUMN_AUTHORITY),
extras,
0 /* delay */);
syncOperation.rowId = cursor.getLong(COLUMN_ID);
return syncOperation;
}
} }
} }

View File

@@ -1,53 +0,0 @@
// Copyright 2007 The Android Open Source Project
package android.content;
import android.database.Cursor;
import android.net.Uri;
/**
* ContentProvider that tracks the sync data and overall sync
* history on the device.
*
* @hide
*/
public class SyncProvider extends ContentProvider {
public SyncProvider() {
}
private SyncStorageEngine mSyncStorageEngine;
@Override
public boolean onCreate() {
mSyncStorageEngine = SyncStorageEngine.getSingleton();
return true;
}
@Override
public Cursor query(Uri url, String[] projectionIn,
String selection, String[] selectionArgs, String sort) {
return mSyncStorageEngine.query(url, projectionIn, selection, selectionArgs, sort);
}
@Override
public Uri insert(Uri url, ContentValues initialValues) {
return mSyncStorageEngine.insert(true /* the caller is the provider */,
url, initialValues);
}
@Override
public int delete(Uri url, String where, String[] whereArgs) {
return mSyncStorageEngine.delete(true /* the caller is the provider */,
url, where, whereArgs);
}
@Override
public int update(Uri url, ContentValues initialValues, String where, String[] whereArgs) {
return mSyncStorageEngine.update(true /* the caller is the provider */,
url, initialValues, where, whereArgs);
}
@Override
public String getType(Uri url) {
return mSyncStorageEngine.getType(url);
}
}

View File

@@ -0,0 +1,19 @@
/*
* 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;
parcelable SyncStatusInfo;

View File

@@ -0,0 +1,108 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
/** @hide */
public class SyncStatusInfo implements Parcelable {
static final int VERSION = 1;
public final int authorityId;
public long totalElapsedTime;
public int numSyncs;
public int numSourcePoll;
public int numSourceServer;
public int numSourceLocal;
public int numSourceUser;
public long lastSuccessTime;
public int lastSuccessSource;
public long lastFailureTime;
public int lastFailureSource;
public String lastFailureMesg;
public long initialFailureTime;
public boolean pending;
SyncStatusInfo(int authorityId) {
this.authorityId = authorityId;
}
public int getLastFailureMesgAsInt(int def) {
try {
if (lastFailureMesg != null) {
return Integer.parseInt(lastFailureMesg);
}
} catch (NumberFormatException e) {
}
return def;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(VERSION);
parcel.writeInt(authorityId);
parcel.writeLong(totalElapsedTime);
parcel.writeInt(numSyncs);
parcel.writeInt(numSourcePoll);
parcel.writeInt(numSourceServer);
parcel.writeInt(numSourceLocal);
parcel.writeInt(numSourceUser);
parcel.writeLong(lastSuccessTime);
parcel.writeInt(lastSuccessSource);
parcel.writeLong(lastFailureTime);
parcel.writeInt(lastFailureSource);
parcel.writeString(lastFailureMesg);
parcel.writeLong(initialFailureTime);
parcel.writeInt(pending ? 1 : 0);
}
SyncStatusInfo(Parcel parcel) {
int version = parcel.readInt();
if (version != VERSION) {
Log.w("SyncStatusInfo", "Unknown version: " + version);
}
authorityId = parcel.readInt();
totalElapsedTime = parcel.readLong();
numSyncs = parcel.readInt();
numSourcePoll = parcel.readInt();
numSourceServer = parcel.readInt();
numSourceLocal = parcel.readInt();
numSourceUser = parcel.readInt();
lastSuccessTime = parcel.readLong();
lastSuccessSource = parcel.readInt();
lastFailureTime = parcel.readLong();
lastFailureSource = parcel.readInt();
lastFailureMesg = parcel.readString();
initialFailureTime = parcel.readLong();
pending = parcel.readInt() != 0;
}
public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
public SyncStatusInfo createFromParcel(Parcel in) {
return new SyncStatusInfo(in);
}
public SyncStatusInfo[] newArray(int size) {
return new SyncStatusInfo[size];
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -49,24 +49,34 @@ import java.util.HashMap;
public class RemoteCallbackList<E extends IInterface> { public class RemoteCallbackList<E extends IInterface> {
/*package*/ HashMap<IBinder, Callback> mCallbacks /*package*/ HashMap<IBinder, Callback> mCallbacks
= new HashMap<IBinder, Callback>(); = new HashMap<IBinder, Callback>();
private IInterface[] mActiveBroadcast; private Object[] mActiveBroadcast;
private boolean mKilled = false; private boolean mKilled = false;
private final class Callback implements IBinder.DeathRecipient { private final class Callback implements IBinder.DeathRecipient {
final E mCallback; final E mCallback;
final Object mCookie;
Callback(E callback) { Callback(E callback, Object cookie) {
mCallback = callback; mCallback = callback;
mCookie = cookie;
} }
public void binderDied() { public void binderDied() {
synchronized (mCallbacks) { synchronized (mCallbacks) {
mCallbacks.remove(mCallback.asBinder()); mCallbacks.remove(mCallback.asBinder());
} }
onCallbackDied(mCallback); onCallbackDied(mCallback, mCookie);
} }
} }
/**
* Simple version of {@link RemoteCallbackList#register(E, Object)}
* that does not take a cookie object.
*/
public boolean register(E callback) {
return register(callback, null);
}
/** /**
* Add a new callback to the list. This callback will remain in the list * Add a new callback to the list. This callback will remain in the list
* until a corresponding call to {@link #unregister} or its hosting process * until a corresponding call to {@link #unregister} or its hosting process
@@ -81,6 +91,8 @@ public class RemoteCallbackList<E extends IInterface> {
* Most services will want to check for null before calling this with * Most services will want to check for null before calling this with
* an object given from a client, so that clients can't crash the * an object given from a client, so that clients can't crash the
* service with bad data. * service with bad data.
* @param cookie Optional additional data to be associated with this
* callback.
* *
* @return Returns true if the callback was successfully added to the list. * @return Returns true if the callback was successfully added to the list.
* Returns false if it was not added, either because {@link #kill} had * Returns false if it was not added, either because {@link #kill} had
@@ -90,14 +102,14 @@ public class RemoteCallbackList<E extends IInterface> {
* @see #kill * @see #kill
* @see #onCallbackDied * @see #onCallbackDied
*/ */
public boolean register(E callback) { public boolean register(E callback, Object cookie) {
synchronized (mCallbacks) { synchronized (mCallbacks) {
if (mKilled) { if (mKilled) {
return false; return false;
} }
IBinder binder = callback.asBinder(); IBinder binder = callback.asBinder();
try { try {
Callback cb = new Callback(callback); Callback cb = new Callback(callback, cookie);
binder.linkToDeath(cb, 0); binder.linkToDeath(cb, 0);
mCallbacks.put(binder, cb); mCallbacks.put(binder, cb);
return true; return true;
@@ -153,18 +165,29 @@ public class RemoteCallbackList<E extends IInterface> {
} }
} }
/**
* Old version of {@link #onCallbackDied(E, Object)} that
* does not provide a cookie.
*/
public void onCallbackDied(E callback) {
}
/** /**
* Called when the process hosting a callback in the list has gone away. * Called when the process hosting a callback in the list has gone away.
* The default implementation does nothing. * The default implementation calls {@link #onCallbackDied(E)}
* for backwards compatibility.
* *
* @param callback The callback whose process has died. Note that, since * @param callback The callback whose process has died. Note that, since
* its process has died, you can not make any calls on to this interface. * its process has died, you can not make any calls on to this interface.
* You can, however, retrieve its IBinder and compare it with another * You can, however, retrieve its IBinder and compare it with another
* IBinder to see if it is the same object. * IBinder to see if it is the same object.
* @param cookie The cookie object original provided to
* {@link #register(E, Object)}.
* *
* @see #register * @see #register
*/ */
public void onCallbackDied(E callback) { public void onCallbackDied(E callback, Object cookie) {
onCallbackDied(callback);
} }
/** /**
@@ -203,13 +226,13 @@ public class RemoteCallbackList<E extends IInterface> {
if (N <= 0) { if (N <= 0) {
return 0; return 0;
} }
IInterface[] active = mActiveBroadcast; Object[] active = mActiveBroadcast;
if (active == null || active.length < N) { if (active == null || active.length < N) {
mActiveBroadcast = active = new IInterface[N]; mActiveBroadcast = active = new Object[N];
} }
int i=0; int i=0;
for (Callback cb : mCallbacks.values()) { for (Callback cb : mCallbacks.values()) {
active[i++] = cb.mCallback; active[i++] = cb;
} }
return i; return i;
} }
@@ -237,7 +260,17 @@ public class RemoteCallbackList<E extends IInterface> {
* @see #beginBroadcast * @see #beginBroadcast
*/ */
public E getBroadcastItem(int index) { public E getBroadcastItem(int index) {
return (E)mActiveBroadcast[index]; return ((Callback)mActiveBroadcast[index]).mCallback;
}
/**
* Retrieve the cookie associated with the item
* returned by {@link #getBroadcastItem(int)}.
*
* @see #getBroadcastItem
*/
public Object getBroadcastCookie(int index) {
return ((Callback)mActiveBroadcast[index]).mCookie;
} }
/** /**
@@ -248,7 +281,7 @@ public class RemoteCallbackList<E extends IInterface> {
* @see #beginBroadcast * @see #beginBroadcast
*/ */
public void finishBroadcast() { public void finishBroadcast() {
IInterface[] active = mActiveBroadcast; Object[] active = mActiveBroadcast;
if (active != null) { if (active != null) {
final int N = active.length; final int N = active.length;
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {

View File

@@ -1,633 +0,0 @@
/*
* Copyright (C) 2007 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.provider;
import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import java.util.Map;
/**
* The Sync provider stores information used in managing the syncing of the device,
* including the history and pending syncs.
*
* @hide
*/
public final class Sync {
// utility class
private Sync() {}
/**
* The content url for this provider.
*/
public static final Uri CONTENT_URI = Uri.parse("content://sync");
/**
* Columns from the stats table.
*/
public interface StatsColumns {
/**
* The sync account.
* <P>Type: TEXT</P>
*/
public static final String ACCOUNT = "account";
/**
* The content authority (contacts, calendar, etc.).
* <P>Type: TEXT</P>
*/
public static final String AUTHORITY = "authority";
}
/**
* Provides constants and utility methods to access and use the stats table.
*/
public static final class Stats implements BaseColumns, StatsColumns {
// utility class
private Stats() {}
/**
* The content url for this table.
*/
public static final Uri CONTENT_URI =
Uri.parse("content://sync/stats");
/** Projection for the _id column in the stats table. */
public static final String[] SYNC_STATS_PROJECTION = {_ID};
}
/**
* Columns from the history table.
*/
public interface HistoryColumns {
/**
* The ID of the stats row corresponding to this event.
* <P>Type: INTEGER</P>
*/
public static final String STATS_ID = "stats_id";
/**
* The source of the sync event (LOCAL, POLL, USER, SERVER).
* <P>Type: INTEGER</P>
*/
public static final String SOURCE = "source";
/**
* The type of sync event (START, STOP).
* <P>Type: INTEGER</P>
*/
public static final String EVENT = "event";
/**
* The time of the event.
* <P>Type: INTEGER</P>
*/
public static final String EVENT_TIME = "eventTime";
/**
* How long this event took. This is only valid if the EVENT is EVENT_STOP.
* <P>Type: INTEGER</P>
*/
public static final String ELAPSED_TIME = "elapsedTime";
/**
* Any additional message associated with this event.
* <P>Type: TEXT</P>
*/
public static final String MESG = "mesg";
/**
* How much activity was performed sending data to the server. This is sync adapter
* specific, but usually is something like how many record update/insert/delete attempts
* were carried out. This is only valid if the EVENT is EVENT_STOP.
* <P>Type: INTEGER</P>
*/
public static final String UPSTREAM_ACTIVITY = "upstreamActivity";
/**
* How much activity was performed while receiving data from the server.
* This is sync adapter specific, but usually is something like how many
* records were received from the server. This is only valid if the
* EVENT is EVENT_STOP.
* <P>Type: INTEGER</P>
*/
public static final String DOWNSTREAM_ACTIVITY = "downstreamActivity";
}
/**
* Columns from the history table.
*/
public interface StatusColumns {
/**
* How many syncs were completed for this account and authority.
* <P>Type: INTEGER</P>
*/
public static final String NUM_SYNCS = "numSyncs";
/**
* How long all the events for this account and authority took.
* <P>Type: INTEGER</P>
*/
public static final String TOTAL_ELAPSED_TIME = "totalElapsedTime";
/**
* The number of syncs with SOURCE_POLL.
* <P>Type: INTEGER</P>
*/
public static final String NUM_SOURCE_POLL = "numSourcePoll";
/**
* The number of syncs with SOURCE_SERVER.
* <P>Type: INTEGER</P>
*/
public static final String NUM_SOURCE_SERVER = "numSourceServer";
/**
* The number of syncs with SOURCE_LOCAL.
* <P>Type: INTEGER</P>
*/
public static final String NUM_SOURCE_LOCAL = "numSourceLocal";
/**
* The number of syncs with SOURCE_USER.
* <P>Type: INTEGER</P>
*/
public static final String NUM_SOURCE_USER = "numSourceUser";
/**
* The time in ms that the last successful sync ended. Will be null if
* there are no successful syncs. A successful sync is defined as one having
* MESG=MESG_SUCCESS.
* <P>Type: INTEGER</P>
*/
public static final String LAST_SUCCESS_TIME = "lastSuccessTime";
/**
* The SOURCE of the last successful sync. Will be null if
* there are no successful syncs. A successful sync is defined
* as one having MESG=MESG_SUCCESS.
* <P>Type: INTEGER</P>
*/
public static final String LAST_SUCCESS_SOURCE = "lastSuccessSource";
/**
* The end time in ms of the last sync that failed since the last successful sync.
* Will be null if there are no syncs or if the last one succeeded. A failed
* sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
* <P>Type: INTEGER</P>
*/
public static final String LAST_FAILURE_TIME = "lastFailureTime";
/**
* The SOURCE of the last sync that failed since the last successful sync.
* Will be null if there are no syncs or if the last one succeeded. A failed
* sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
* <P>Type: INTEGER</P>
*/
public static final String LAST_FAILURE_SOURCE = "lastFailureSource";
/**
* The MESG of the last sync that failed since the last successful sync.
* Will be null if there are no syncs or if the last one succeeded. A failed
* sync is defined as one where MESG isn't MESG_SUCCESS or MESG_CANCELED.
* <P>Type: STRING</P>
*/
public static final String LAST_FAILURE_MESG = "lastFailureMesg";
/**
* Is set to 1 if a sync is pending, 0 if not.
* <P>Type: INTEGER</P>
*/
public static final String PENDING = "pending";
}
/**
* Provides constants and utility methods to access and use the history
* table.
*/
public static class History implements BaseColumns,
StatsColumns,
HistoryColumns {
/**
* The content url for this table.
*/
public static final Uri CONTENT_URI =
Uri.parse("content://sync/history");
/** Enum value for a sync start event. */
public static final int EVENT_START = 0;
/** Enum value for a sync stop event. */
public static final int EVENT_STOP = 1;
// TODO: i18n -- grab these out of resources.
/** String names for the sync event types. */
public static final String[] EVENTS = { "START", "STOP" };
/** Enum value for a server-initiated sync. */
public static final int SOURCE_SERVER = 0;
/** Enum value for a local-initiated sync. */
public static final int SOURCE_LOCAL = 1;
/**
* Enum value for a poll-based sync (e.g., upon connection to
* network)
*/
public static final int SOURCE_POLL = 2;
/** Enum value for a user-initiated sync. */
public static final int SOURCE_USER = 3;
// TODO: i18n -- grab these out of resources.
/** String names for the sync source types. */
public static final String[] SOURCES = { "SERVER",
"LOCAL",
"POLL",
"USER" };
// Error types
public static final int ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
public static final int ERROR_AUTHENTICATION = 2;
public static final int ERROR_IO = 3;
public static final int ERROR_PARSE = 4;
public static final int ERROR_CONFLICT = 5;
public static final int ERROR_TOO_MANY_DELETIONS = 6;
public static final int ERROR_TOO_MANY_RETRIES = 7;
public static final int ERROR_INTERNAL = 8;
// The MESG column will contain one of these or one of the Error types.
public static final String MESG_SUCCESS = "success";
public static final String MESG_CANCELED = "canceled";
private static final String FINISHED_SINCE_WHERE_CLAUSE = EVENT + "=" + EVENT_STOP
+ " AND " + EVENT_TIME + ">? AND " + ACCOUNT + "=? AND " + AUTHORITY + "=?";
public static String mesgToString(String mesg) {
if (MESG_SUCCESS.equals(mesg)) return mesg;
if (MESG_CANCELED.equals(mesg)) return mesg;
switch (Integer.parseInt(mesg)) {
case ERROR_SYNC_ALREADY_IN_PROGRESS: return "already in progress";
case ERROR_AUTHENTICATION: return "bad authentication";
case ERROR_IO: return "network error";
case ERROR_PARSE: return "parse error";
case ERROR_CONFLICT: return "conflict detected";
case ERROR_TOO_MANY_DELETIONS: return "too many deletions";
case ERROR_TOO_MANY_RETRIES: return "too many retries";
case ERROR_INTERNAL: return "internal error";
default: return "unknown error";
}
}
// utility class
private History() {}
/**
* returns a cursor that queries the sync history in descending event time order
* @param contentResolver the ContentResolver to use for the query
* @return the cursor on the History table
*/
public static Cursor query(ContentResolver contentResolver) {
return contentResolver.query(CONTENT_URI, null, null, null, EVENT_TIME + " desc");
}
public static boolean hasNewerSyncFinished(ContentResolver contentResolver,
String account, String authority, long when) {
Cursor c = contentResolver.query(CONTENT_URI, new String[]{_ID},
FINISHED_SINCE_WHERE_CLAUSE,
new String[]{Long.toString(when), account, authority}, null);
try {
return c.getCount() > 0;
} finally {
c.close();
}
}
}
/**
* Provides constants and utility methods to access and use the authority history
* table, which contains information about syncs aggregated by account and authority.
* All the HistoryColumns except for EVENT are present, plus the AuthorityHistoryColumns.
*/
public static class Status extends History implements StatusColumns {
/**
* The content url for this table.
*/
public static final Uri CONTENT_URI = Uri.parse("content://sync/status");
// utility class
private Status() {}
/**
* returns a cursor that queries the authority sync history in descending event order of
* ACCOUNT, AUTHORITY
* @param contentResolver the ContentResolver to use for the query
* @return the cursor on the AuthorityHistory table
*/
public static Cursor query(ContentResolver contentResolver) {
return contentResolver.query(CONTENT_URI, null, null, null, ACCOUNT + ", " + AUTHORITY);
}
public static class QueryMap extends ContentQueryMap {
public QueryMap(ContentResolver contentResolver,
boolean keepUpdated,
Handler handlerForUpdateNotifications) {
super(contentResolver.query(CONTENT_URI, null, null, null, null),
_ID, keepUpdated, handlerForUpdateNotifications);
}
public ContentValues get(String account, String authority) {
Map<String, ContentValues> rows = getRows();
for (ContentValues values : rows.values()) {
if (values.getAsString(ACCOUNT).equals(account)
&& values.getAsString(AUTHORITY).equals(authority)) {
return values;
}
}
return null;
}
}
}
/**
* Provides constants and utility methods to access and use the pending syncs table
*/
public static final class Pending implements BaseColumns,
StatsColumns {
/**
* The content url for this table.
*/
public static final Uri CONTENT_URI = Uri.parse("content://sync/pending");
// utility class
private Pending() {}
public static class QueryMap extends ContentQueryMap {
public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
Handler handlerForUpdateNotifications) {
super(contentResolver.query(CONTENT_URI, null, null, null, null), _ID, keepUpdated,
handlerForUpdateNotifications);
}
public boolean isPending(String account, String authority) {
Map<String, ContentValues> rows = getRows();
for (ContentValues values : rows.values()) {
if (values.getAsString(ACCOUNT).equals(account)
&& values.getAsString(AUTHORITY).equals(authority)) {
return true;
}
}
return false;
}
}
}
/**
* Columns from the history table.
*/
public interface ActiveColumns {
/**
* The wallclock time of when the active sync started.
* <P>Type: INTEGER</P>
*/
public static final String START_TIME = "startTime";
}
/**
* Provides constants and utility methods to access and use the pending syncs table
*/
public static final class Active implements BaseColumns,
StatsColumns,
ActiveColumns {
/**
* The content url for this table.
*/
public static final Uri CONTENT_URI = Uri.parse("content://sync/active");
// utility class
private Active() {}
public static class QueryMap extends ContentQueryMap {
public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
Handler handlerForUpdateNotifications) {
super(contentResolver.query(CONTENT_URI, null, null, null, null), _ID, keepUpdated,
handlerForUpdateNotifications);
}
public ContentValues getActiveSyncInfo() {
Map<String, ContentValues> rows = getRows();
for (ContentValues values : rows.values()) {
return values;
}
return null;
}
public String getSyncingAccount() {
ContentValues values = getActiveSyncInfo();
return (values == null) ? null : values.getAsString(ACCOUNT);
}
public String getSyncingAuthority() {
ContentValues values = getActiveSyncInfo();
return (values == null) ? null : values.getAsString(AUTHORITY);
}
public long getSyncStartTime() {
ContentValues values = getActiveSyncInfo();
return (values == null) ? -1 : values.getAsLong(START_TIME);
}
}
}
/**
* Columns in the settings table, which holds key/value pairs of settings.
*/
public interface SettingsColumns {
/**
* The key of the setting
* <P>Type: TEXT</P>
*/
public static final String KEY = "name";
/**
* The value of the settings
* <P>Type: TEXT</P>
*/
public static final String VALUE = "value";
}
/**
* Provides constants and utility methods to access and use the settings
* table.
*/
public static final class Settings implements BaseColumns, SettingsColumns {
/**
* The Uri of the settings table. This table behaves a little differently than
* normal tables. Updates are not allowed, only inserts, and inserts cause a replace
* to be performed, which first deletes the row if it is already present.
*/
public static final Uri CONTENT_URI = Uri.parse("content://sync/settings");
/** controls whether or not the device listens for sync tickles */
public static final String SETTING_LISTEN_FOR_TICKLES = "listen_for_tickles";
/** controls whether or not the individual provider is synced when tickles are received */
public static final String SETTING_SYNC_PROVIDER_PREFIX = "sync_provider_";
/** query column project */
private static final String[] PROJECTION = { KEY, VALUE };
/**
* Convenience function for updating a single settings value as a
* boolean. This will either create a new entry in the table if the
* given name does not exist, or modify the value of the existing row
* with that name. Note that internally setting values are always
* stored as strings, so this function converts the given value to a
* string before storing it.
*
* @param contentResolver the ContentResolver to use to access the settings table
* @param name The name of the setting to modify.
* @param val The new value for the setting.
*/
static private void putBoolean(ContentResolver contentResolver, String name, boolean val) {
ContentValues values = new ContentValues();
values.put(KEY, name);
values.put(VALUE, Boolean.toString(val));
// this insert is translated into an update by the underlying Sync provider
contentResolver.insert(CONTENT_URI, values);
}
/**
* Convenience function for getting a setting value as a boolean without using the
* QueryMap for light-weight setting querying.
* @param contentResolver The ContentResolver for querying the setting.
* @param name The name of the setting to query
* @param def The default value for the setting.
* @return The value of the setting.
*/
static public boolean getBoolean(ContentResolver contentResolver,
String name, boolean def) {
Cursor cursor = contentResolver.query(
CONTENT_URI,
PROJECTION,
KEY + "=?",
new String[] { name },
null);
try {
if (cursor != null && cursor.moveToFirst()) {
return Boolean.parseBoolean(cursor.getString(1));
}
} finally {
if (cursor != null) cursor.close();
}
return def;
}
/**
* A convenience method to set whether or not the provider is synced when
* it receives a network tickle.
*
* @param contentResolver the ContentResolver to use to access the settings table
* @param providerName the provider whose behavior is being controlled
* @param sync true if the provider should be synced when tickles are received for it
*/
static public void setSyncProviderAutomatically(ContentResolver contentResolver,
String providerName, boolean sync) {
putBoolean(contentResolver, SETTING_SYNC_PROVIDER_PREFIX + providerName, sync);
}
/**
* A convenience method to set whether or not the device should listen to tickles.
*
* @param contentResolver the ContentResolver to use to access the settings table
* @param flag true if it should listen.
*/
static public void setListenForNetworkTickles(ContentResolver contentResolver,
boolean flag) {
putBoolean(contentResolver, SETTING_LISTEN_FOR_TICKLES, flag);
}
public static class QueryMap extends ContentQueryMap {
private ContentResolver mContentResolver;
public QueryMap(ContentResolver contentResolver, boolean keepUpdated,
Handler handlerForUpdateNotifications) {
super(contentResolver.query(CONTENT_URI, null, null, null, null), KEY, keepUpdated,
handlerForUpdateNotifications);
mContentResolver = contentResolver;
}
/**
* Check if the provider should be synced when a network tickle is received
* @param providerName the provider whose setting we are querying
* @return true of the provider should be synced when a network tickle is received
*/
public boolean getSyncProviderAutomatically(String providerName) {
return getBoolean(SETTING_SYNC_PROVIDER_PREFIX + providerName, true);
}
/**
* Set whether or not the provider is synced when it receives a network tickle.
*
* @param providerName the provider whose behavior is being controlled
* @param sync true if the provider should be synced when tickles are received for it
*/
public void setSyncProviderAutomatically(String providerName, boolean sync) {
Settings.setSyncProviderAutomatically(mContentResolver, providerName, sync);
}
/**
* Set whether or not the device should listen for tickles.
*
* @param flag true if it should listen.
*/
public void setListenForNetworkTickles(boolean flag) {
Settings.setListenForNetworkTickles(mContentResolver, flag);
}
/**
* Check if the device should listen to tickles.
* @return true if it should
*/
public boolean getListenForNetworkTickles() {
return getBoolean(SETTING_LISTEN_FOR_TICKLES, true);
}
/**
* Convenience function for retrieving a single settings value
* as a boolean.
*
* @param name The name of the setting to retrieve.
* @param def Value to return if the setting is not defined.
* @return The setting's current value, or 'def' if it is not defined.
*/
private boolean getBoolean(String name, boolean def) {
ContentValues values = getValues(name);
return values != null ? values.getAsBoolean(VALUE) : def;
}
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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 com.android.internal.os;
import android.os.FileUtils;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Helper class for performing atomic operations on a file, by creating a
* backup file until a write has successfully completed.
*/
public class AtomicFile {
private final File mBaseName;
private final File mBackupName;
public AtomicFile(File baseName) {
mBaseName = baseName;
mBackupName = new File(baseName.getPath() + ".bak");
}
public File getBaseFile() {
return mBaseName;
}
public FileOutputStream startWrite() throws IOException {
// Rename the current file so it may be used as a backup during the next read
if (mBaseName.exists()) {
if (!mBaseName.renameTo(mBackupName)) {
mBackupName.delete();
if (!mBaseName.renameTo(mBackupName)) {
Log.w("AtomicFile", "Couldn't rename file " + mBaseName
+ " to backup file " + mBackupName);
}
}
}
FileOutputStream str = null;
try {
str = new FileOutputStream(mBaseName);
} catch (FileNotFoundException e) {
File parent = mBaseName.getParentFile();
if (!parent.mkdir()) {
throw new IOException("Couldn't create directory " + mBaseName);
}
FileUtils.setPermissions(
parent.getPath(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-1, -1);
try {
str = new FileOutputStream(mBaseName);
} catch (FileNotFoundException e2) {
throw new IOException("Couldn't create " + mBaseName);
}
}
return str;
}
public void finishWrite(FileOutputStream str) {
if (str != null) {
try {
str.close();
mBackupName.delete();
} catch (IOException e) {
Log.w("AtomicFile", "finishWrite: Got exception:", e);
}
}
}
public void failWrite(FileOutputStream str) {
if (str != null) {
try {
str.close();
mBaseName.delete();
mBackupName.renameTo(mBaseName);
} catch (IOException e) {
Log.w("AtomicFile", "failWrite: Got exception:", e);
}
}
}
public FileOutputStream openAppend() throws IOException {
try {
return new FileOutputStream(mBaseName, true);
} catch (FileNotFoundException e) {
throw new IOException("Couldn't append " + mBaseName);
}
}
public void truncate() throws IOException {
try {
FileOutputStream fos = new FileOutputStream(mBaseName);
fos.close();
} catch (FileNotFoundException e) {
throw new IOException("Couldn't append " + mBaseName);
} catch (IOException e) {
}
}
public FileInputStream openRead() throws FileNotFoundException {
if (mBackupName.exists()) {
mBaseName.delete();
mBackupName.renameTo(mBaseName);
}
return new FileInputStream(mBaseName);
}
public byte[] readFully() throws IOException {
FileInputStream stream = openRead();
try {
int pos = 0;
int avail = stream.available();
byte[] data = new byte[avail];
while (true) {
int amt = stream.read(data, pos, data.length-pos);
//Log.i("foo", "Read " + amt + " bytes at " + pos
// + " of avail " + data.length);
if (amt <= 0) {
//Log.i("foo", "**** FINISHED READING: pos=" + pos
// + " len=" + data.length);
return data;
}
pos += amt;
avail = stream.available();
if (avail > data.length-pos) {
byte[] newData = new byte[pos+avail];
System.arraycopy(data, 0, newData, 0, pos);
data = newData;
}
}
} finally {
stream.close();
}
}
}

View File

@@ -1012,9 +1012,6 @@
android:excludeFromRecents="true"> android:excludeFromRecents="true">
</activity> </activity>
<provider android:name=".content.SyncProvider"
android:authorities="sync" android:multiprocess="false" />
<service android:name="com.android.server.LoadAverageService" <service android:name="com.android.server.LoadAverageService"
android:exported="true" /> android:exported="true" />

View File

@@ -9,7 +9,6 @@ import android.util.Log;
import android.util.Config; import android.util.Config;
import android.util.EventLog; import android.util.EventLog;
import android.app.IntentService; import android.app.IntentService;
import android.provider.Sync;
import android.provider.SubscribedFeeds; import android.provider.SubscribedFeeds;
import android.provider.SyncConstValue; import android.provider.SyncConstValue;
import android.database.Cursor; import android.database.Cursor;
@@ -17,7 +16,7 @@ import android.database.sqlite.SQLiteFullException;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Debug; import android.os.RemoteException;
import android.text.TextUtils; import android.text.TextUtils;
import android.net.Uri; import android.net.Uri;
@@ -105,10 +104,6 @@ public class SubscribedFeedsIntentService extends IntentService {
private void handleTickle(Context context, String account, String feed) { private void handleTickle(Context context, String account, String feed) {
Cursor c = null; Cursor c = null;
Sync.Settings.QueryMap syncSettings =
new Sync.Settings.QueryMap(context.getContentResolver(),
false /* don't keep updated */,
null /* not needed since keep updated is false */);
final String where = SubscribedFeeds.Feeds._SYNC_ACCOUNT + "= ? " final String where = SubscribedFeeds.Feeds._SYNC_ACCOUNT + "= ? "
+ "and " + SubscribedFeeds.Feeds.FEED + "= ?"; + "and " + SubscribedFeeds.Feeds.FEED + "= ?";
try { try {
@@ -124,9 +119,14 @@ public class SubscribedFeedsIntentService extends IntentService {
String authority = c.getString(c.getColumnIndexOrThrow( String authority = c.getString(c.getColumnIndexOrThrow(
SubscribedFeeds.Feeds.AUTHORITY)); SubscribedFeeds.Feeds.AUTHORITY));
EventLog.writeEvent(LOG_TICKLE, authority); EventLog.writeEvent(LOG_TICKLE, authority);
if (!syncSettings.getSyncProviderAutomatically(authority)) { try {
Log.d(TAG, "supressing tickle since provider " + authority if (!ContentResolver.getContentService()
+ " is configured to not sync automatically"); .getSyncProviderAutomatically(authority)) {
Log.d(TAG, "supressing tickle since provider " + authority
+ " is configured to not sync automatically");
continue;
}
} catch (RemoteException e) {
continue; continue;
} }
Uri uri = Uri.parse("content://" + authority); Uri uri = Uri.parse("content://" + authority);
@@ -137,7 +137,6 @@ public class SubscribedFeedsIntentService extends IntentService {
} }
} finally { } finally {
if (c != null) c.deactivate(); if (c != null) c.deactivate();
syncSettings.close();
} }
} }

View File

@@ -76,8 +76,6 @@ android.content.ContentQueryMap
android.content.ContentQueryMap$1 android.content.ContentQueryMap$1
android.content.ContentResolver android.content.ContentResolver
android.content.ContentResolver$CursorWrapperInner android.content.ContentResolver$CursorWrapperInner
android.content.ContentServiceNative
android.content.ContentServiceProxy
android.content.ContentValues android.content.ContentValues
android.content.Context android.content.Context
android.content.ContextWrapper android.content.ContextWrapper
@@ -85,6 +83,8 @@ android.content.DialogInterface
android.content.DialogInterface$OnCancelListener android.content.DialogInterface$OnCancelListener
android.content.DialogInterface$OnDismissListener android.content.DialogInterface$OnDismissListener
android.content.IContentProvider android.content.IContentProvider
android.content.IContentService
android.content.IContentService$Stub
android.content.Intent android.content.Intent
android.content.Intent$1 android.content.Intent$1
android.content.IntentFilter android.content.IntentFilter

View File

@@ -35,7 +35,6 @@ import android.os.Message;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.provider.Settings; import android.provider.Settings;
import android.provider.Sync;
import android.util.EventLog; import android.util.EventLog;
import android.util.Log; import android.util.Log;

View File

@@ -18,12 +18,10 @@ package android.test;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.ContentValues;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock; import android.os.SystemClock;
import android.provider.Sync;
import android.net.Uri; import android.net.Uri;
import java.util.Map;
/** /**
* If you would like to test sync a single provider with an * If you would like to test sync a single provider with an
@@ -75,11 +73,11 @@ public class SyncBaseInstrumentation extends InstrumentationTestCase {
} }
protected void cancelSyncsandDisableAutoSync() { protected void cancelSyncsandDisableAutoSync() {
Sync.Settings.QueryMap mSyncSettings = try {
new Sync.Settings.QueryMap(mContentResolver, true, null); ContentResolver.getContentService().setListenForNetworkTickles(false);
mSyncSettings.setListenForNetworkTickles(false); } catch (RemoteException e) {
}
mContentResolver.cancelSync(null); mContentResolver.cancelSync(null);
mSyncSettings.close();
} }
/** /**
@@ -88,34 +86,11 @@ public class SyncBaseInstrumentation extends InstrumentationTestCase {
* @return * @return
*/ */
private boolean isSyncActive(String account, String authority) { private boolean isSyncActive(String account, String authority) {
Sync.Pending.QueryMap pendingQueryMap = null;
Sync.Active.QueryMap activeQueryMap = null;
try { try {
pendingQueryMap = new Sync.Pending.QueryMap(mContentResolver, false, null); return ContentResolver.getContentService().isSyncActive(account,
activeQueryMap = new Sync.Active.QueryMap(mContentResolver, false, null); authority);
} catch (RemoteException e) {
if (pendingQueryMap.isPending(account, authority)) {
return true;
}
if (isActiveInActiveQueryMap(activeQueryMap, account, authority)) {
return true;
}
return false; return false;
} finally {
activeQueryMap.close();
pendingQueryMap.close();
} }
} }
private boolean isActiveInActiveQueryMap(Sync.Active.QueryMap activemap, String account,
String authority) {
Map<String, ContentValues> rows = activemap.getRows();
for (ContentValues values : rows.values()) {
if (values.getAsString("account").equals(account)
&& values.getAsString("authority").equals(authority)) {
return true;
}
}
return false;
}
} }

View File

@@ -20,7 +20,6 @@ import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext; import android.test.RenamingDelegatingContext;
import android.test.mock.MockContext; import android.test.mock.MockContext;
import android.test.mock.MockContentResolver; import android.test.mock.MockContentResolver;
import android.provider.Sync;
public class SyncStorageEngineTest extends AndroidTestCase { public class SyncStorageEngineTest extends AndroidTestCase {
@@ -39,7 +38,7 @@ public class SyncStorageEngineTest extends AndroidTestCase {
long time0 = 1000; long time0 = 1000;
long historyId = engine.insertStartSyncEvent( long historyId = engine.insertStartSyncEvent(
account, authority, time0, Sync.History.SOURCE_LOCAL); account, authority, time0, SyncStorageEngine.SOURCE_LOCAL);
long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2; long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0); engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
} }

View File

@@ -17,7 +17,6 @@
package com.android.layoutlib.bridge; package com.android.layoutlib.bridge;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentServiceNative;
import android.content.Context; import android.content.Context;
import android.content.IContentProvider; import android.content.IContentProvider;
import android.database.ContentObserver; import android.database.ContentObserver;
@@ -51,9 +50,6 @@ public class BridgeContentResolver extends ContentResolver {
/** /**
* Stub for the layoutlib bridge content resolver. * Stub for the layoutlib bridge content resolver.
* <p/>
* The super implementation accesses the {@link ContentServiceNative#getDefault()}
* which returns null and would make the call crash. Instead we do nothing.
*/ */
@Override @Override
public void registerContentObserver(Uri uri, boolean notifyForDescendents, public void registerContentObserver(Uri uri, boolean notifyForDescendents,
@@ -63,9 +59,6 @@ public class BridgeContentResolver extends ContentResolver {
/** /**
* Stub for the layoutlib bridge content resolver. * Stub for the layoutlib bridge content resolver.
* <p/>
* The super implementation accesses the {@link ContentServiceNative#getDefault()}
* which returns null and would make the call crash. Instead we do nothing.
*/ */
@Override @Override
public void unregisterContentObserver(ContentObserver observer) { public void unregisterContentObserver(ContentObserver observer) {
@@ -74,9 +67,6 @@ public class BridgeContentResolver extends ContentResolver {
/** /**
* Stub for the layoutlib bridge content resolver. * Stub for the layoutlib bridge content resolver.
* <p/>
* The super implementation accesses the {@link ContentServiceNative#getDefault()}
* which returns null and would make the call crash. Instead we do nothing.
*/ */
@Override @Override
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
@@ -85,9 +75,6 @@ public class BridgeContentResolver extends ContentResolver {
/** /**
* Stub for the layoutlib bridge content resolver. * Stub for the layoutlib bridge content resolver.
* <p/>
* The super implementation accesses the {@link ContentServiceNative#getDefault()}
* which returns null and would make the call crash. Instead we do nothing.
*/ */
@Override @Override
public void startSync(Uri uri, Bundle extras) { public void startSync(Uri uri, Bundle extras) {
@@ -96,9 +83,6 @@ public class BridgeContentResolver extends ContentResolver {
/** /**
* Stub for the layoutlib bridge content resolver. * Stub for the layoutlib bridge content resolver.
* <p/>
* The super implementation accesses the {@link ContentServiceNative#getDefault()}
* which returns null and would make the call crash. Instead we do nothing.
*/ */
@Override @Override
public void cancelSync(Uri uri) { public void cancelSync(Uri uri) {