Rewrite SyncStorageEngine to use flat files and in-memory data structures.
The previous implementation used a database for storing all of its state, which could cause a significant amount of IO activity as its tables were updated through the stages of a sync. This new implementation replaces that in-memory data structures, with hand-written code for writing them to persistent storage. There are now 4 files associated with this class, holding various pieces of its state that should be consistent. These are everything from a main XML file of account information that must always be retained, to a binary file of per-day statistics that can be thrown away at any time. Writes of these files as scheduled at various times based on their importance of the frequency at which they change. Because the database no longer exists, there needs to be a new explicit interface for interacting with the sync manager database. This is provided by new APIs on IContentService, with a hidden method on ContentResolver to retrieve the IContentService so that various system entities can use it. Other changes in other projects are required to update to the new API. The goal here is to have as little an impact on the code and functionality outside of SyncStorageEngine, though due to the necessary change in API it is still somewhat extensive.
This commit is contained in:
@@ -85,8 +85,10 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/bluetooth/IBluetoothDevice.aidl \
|
||||
core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
|
||||
core/java/android/bluetooth/IBluetoothHeadset.aidl \
|
||||
core/java/android/content/IContentService.aidl \
|
||||
core/java/android/content/ISyncAdapter.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/IPackageDeleteObserver.aidl \
|
||||
core/java/android/content/pm/IPackageInstallObserver.aidl \
|
||||
|
||||
@@ -92268,6 +92268,19 @@
|
||||
visibility="public"
|
||||
>
|
||||
</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"
|
||||
return="E"
|
||||
abstract="false"
|
||||
@@ -92305,6 +92318,21 @@
|
||||
<parameter name="callback" type="E">
|
||||
</parameter>
|
||||
</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"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
@@ -92318,6 +92346,21 @@
|
||||
<parameter name="callback" type="E">
|
||||
</parameter>
|
||||
</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"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
|
||||
19
core/java/android/content/ActiveSyncInfo.aidl
Normal file
19
core/java/android/content/ActiveSyncInfo.aidl
Normal 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;
|
||||
64
core/java/android/content/ActiveSyncInfo.java
Normal file
64
core/java/android/content/ActiveSyncInfo.java
Normal 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];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -25,9 +25,13 @@ import android.database.CursorWrapper;
|
||||
import android.database.IContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
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 ContentResolver(Context context)
|
||||
{
|
||||
public ContentResolver(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@@ -605,7 +608,7 @@ public abstract class ContentResolver {
|
||||
ContentObserver observer)
|
||||
{
|
||||
try {
|
||||
ContentServiceNative.getDefault().registerContentObserver(uri, notifyForDescendents,
|
||||
getContentService().registerContentObserver(uri, notifyForDescendents,
|
||||
observer.getContentObserver());
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
@@ -621,7 +624,7 @@ public abstract class ContentResolver {
|
||||
try {
|
||||
IContentObserver contentObserver = observer.releaseContentObserver();
|
||||
if (contentObserver != null) {
|
||||
ContentServiceNative.getDefault().unregisterContentObserver(
|
||||
getContentService().unregisterContentObserver(
|
||||
contentObserver);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
@@ -651,7 +654,7 @@ public abstract class ContentResolver {
|
||||
*/
|
||||
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
|
||||
try {
|
||||
ContentServiceNative.getDefault().notifyChange(
|
||||
getContentService().notifyChange(
|
||||
uri, observer == null ? null : observer.getContentObserver(),
|
||||
observer != null && observer.deliverSelfNotifications(), syncToNetwork);
|
||||
} catch (RemoteException e) {
|
||||
@@ -677,7 +680,7 @@ public abstract class ContentResolver {
|
||||
public void startSync(Uri uri, Bundle extras) {
|
||||
validateSyncExtrasBundle(extras);
|
||||
try {
|
||||
ContentServiceNative.getDefault().startSync(uri, extras);
|
||||
getContentService().startSync(uri, extras);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
@@ -718,7 +721,7 @@ public abstract class ContentResolver {
|
||||
|
||||
public void cancelSync(Uri uri) {
|
||||
try {
|
||||
ContentServiceNative.getDefault().cancelSync(uri);
|
||||
getContentService().cancelSync(uri);
|
||||
} 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 static final String TAG = "ContentResolver";
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Config;
|
||||
@@ -34,7 +35,7 @@ import java.util.ArrayList;
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
public final class ContentService extends ContentServiceNative {
|
||||
public final class ContentService extends IContentService.Stub {
|
||||
private static final String TAG = "ContentService";
|
||||
private Context mContext;
|
||||
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) {
|
||||
mContext = context;
|
||||
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) {
|
||||
ContentService service = new ContentService(context, factoryTest);
|
||||
ServiceManager.addService("content", service);
|
||||
ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
83
core/java/android/content/IContentService.aidl
Normal file
83
core/java/android/content/IContentService.aidl
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
24
core/java/android/content/ISyncStatusObserver.aidl
Normal file
24
core/java/android/content/ISyncStatusObserver.aidl
Normal 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);
|
||||
}
|
||||
@@ -32,8 +32,6 @@ import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
@@ -43,18 +41,13 @@ import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceGroup;
|
||||
import android.provider.Sync;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Sync.History;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
@@ -73,13 +66,12 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Random;
|
||||
import java.util.Observer;
|
||||
import java.util.Observable;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
@@ -138,7 +130,6 @@ class SyncManager {
|
||||
volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
|
||||
volatile private boolean mDataConnectionIsConnected = false;
|
||||
volatile private boolean mStorageIsLow = false;
|
||||
private Sync.Settings.QueryMap mSyncSettings;
|
||||
|
||||
private final NotificationManager mNotificationMgr;
|
||||
private AlarmManager mAlarmService = null;
|
||||
@@ -225,17 +216,6 @@ class SyncManager {
|
||||
private static final String SYNC_POLL_ALARM = "android.content.syncmanager.SYNC_POLL_ALARM";
|
||||
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 MIN_SYNC_POLL_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
|
||||
|
||||
@@ -289,6 +269,14 @@ class SyncManager {
|
||||
HANDLE_SYNC_ALARM_WAKE_LOCK);
|
||||
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) {
|
||||
AccountMonitorListener listener = new AccountMonitorListener() {
|
||||
public void onAccountsUpdated(String[] accounts) {
|
||||
@@ -448,20 +436,10 @@ class SyncManager {
|
||||
return mActiveSyncContext;
|
||||
}
|
||||
|
||||
private Sync.Settings.QueryMap getSyncSettings() {
|
||||
if (mSyncSettings == null) {
|
||||
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;
|
||||
public SyncStorageEngine getSyncStorageEngine() {
|
||||
return mSyncStorageEngine;
|
||||
}
|
||||
|
||||
|
||||
private void ensureContentResolver() {
|
||||
if (mContentResolver == null) {
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
@@ -574,15 +552,15 @@ class SyncManager {
|
||||
|
||||
int source;
|
||||
if (uploadOnly) {
|
||||
source = Sync.History.SOURCE_LOCAL;
|
||||
source = SyncStorageEngine.SOURCE_LOCAL;
|
||||
} else if (force) {
|
||||
source = Sync.History.SOURCE_USER;
|
||||
source = SyncStorageEngine.SOURCE_USER;
|
||||
} else if (url == null) {
|
||||
source = Sync.History.SOURCE_POLL;
|
||||
source = SyncStorageEngine.SOURCE_POLL;
|
||||
} else {
|
||||
// this isn't strictly server, since arbitrary callers can (and do) request
|
||||
// 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>();
|
||||
@@ -667,9 +645,7 @@ class SyncManager {
|
||||
|
||||
public void updateHeartbeatTime() {
|
||||
mHeartbeatTime = SystemClock.elapsedRealtime();
|
||||
ensureContentResolver();
|
||||
mContentResolver.notifyChange(Sync.Active.CONTENT_URI,
|
||||
null /* this change wasn't made through an observer */);
|
||||
mSyncStorageEngine.reportActiveChange();
|
||||
}
|
||||
|
||||
private void sendSyncAlarmMessage() {
|
||||
@@ -876,7 +852,7 @@ class SyncManager {
|
||||
final String key;
|
||||
long earliestRunTime;
|
||||
long delay;
|
||||
Long rowId = null;
|
||||
SyncStorageEngine.PendingOperation pendingOperation;
|
||||
|
||||
SyncOperation(String account, int source, String authority, Bundle extras, long delay) {
|
||||
this.account = account;
|
||||
@@ -916,7 +892,7 @@ class SyncManager {
|
||||
sb.append(" when: ").append(earliestRunTime);
|
||||
sb.append(" delay: ").append(delay);
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -999,242 +975,262 @@ class SyncManager {
|
||||
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
dumpSyncState(sb);
|
||||
sb.append("\n");
|
||||
dumpSyncState(pw, sb);
|
||||
if (isSyncEnabled()) {
|
||||
dumpSyncHistory(sb);
|
||||
dumpSyncHistory(pw, sb);
|
||||
}
|
||||
pw.println(sb.toString());
|
||||
}
|
||||
|
||||
protected void dumpSyncState(StringBuilder sb) {
|
||||
sb.append("sync enabled: ").append(isSyncEnabled()).append("\n");
|
||||
sb.append("data connected: ").append(mDataConnectionIsConnected).append("\n");
|
||||
sb.append("memory low: ").append(mStorageIsLow).append("\n");
|
||||
static String formatTime(long time) {
|
||||
Time tobj = new Time();
|
||||
tobj.set(time);
|
||||
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;
|
||||
sb.append("accounts: ");
|
||||
pw.print("accounts: ");
|
||||
if (accounts != null) {
|
||||
sb.append(accounts.length);
|
||||
pw.println(accounts.length);
|
||||
} else {
|
||||
sb.append("none");
|
||||
pw.println("none");
|
||||
}
|
||||
sb.append("\n");
|
||||
final long now = SystemClock.elapsedRealtime();
|
||||
sb.append("now: ").append(now).append("\n");
|
||||
sb.append("uptime: ").append(DateUtils.formatElapsedTime(now/1000)).append(" (HH:MM:SS)\n");
|
||||
sb.append("time spent syncing : ")
|
||||
.append(DateUtils.formatElapsedTime(
|
||||
mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000))
|
||||
.append(" (HH:MM:SS), sync ")
|
||||
.append(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ")
|
||||
.append("in progress").append("\n");
|
||||
pw.print("now: "); pw.println(now);
|
||||
pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000));
|
||||
pw.println(" (HH:MM:SS)");
|
||||
pw.print("time spent syncing: ");
|
||||
pw.print(DateUtils.formatElapsedTime(
|
||||
mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
|
||||
pw.print(" (HH:MM:SS), sync ");
|
||||
pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
|
||||
pw.println("in progress");
|
||||
if (mSyncHandler.mAlarmScheduleTime != null) {
|
||||
sb.append("next alarm time: ").append(mSyncHandler.mAlarmScheduleTime)
|
||||
.append(" (")
|
||||
.append(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000))
|
||||
.append(" (HH:MM:SS) from now)\n");
|
||||
pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime);
|
||||
pw.print(" (");
|
||||
pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000));
|
||||
pw.println(" (HH:MM:SS) from now)");
|
||||
} 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);
|
||||
sb.append("\n");
|
||||
pw.println(sb.toString());
|
||||
|
||||
synchronized (mSyncQueue) {
|
||||
sb.append("sync queue: ");
|
||||
pw.print("sync queue: ");
|
||||
sb.setLength(0);
|
||||
mSyncQueue.dump(sb);
|
||||
pw.println(sb.toString());
|
||||
}
|
||||
|
||||
Cursor c = mSyncStorageEngine.query(Sync.Active.CONTENT_URI,
|
||||
SYNC_ACTIVE_PROJECTION, null, null, null);
|
||||
sb.append("\n");
|
||||
try {
|
||||
if (c.moveToNext()) {
|
||||
final long durationInSeconds = (now - c.getLong(2)) / 1000;
|
||||
sb.append("Active sync: ").append(c.getString(0))
|
||||
.append(" ").append(c.getString(1))
|
||||
.append(", duration is ")
|
||||
.append(DateUtils.formatElapsedTime(durationInSeconds)).append(".\n");
|
||||
} else {
|
||||
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));
|
||||
ActiveSyncInfo active = mSyncStorageEngine.getActiveSync();
|
||||
if (active != null) {
|
||||
SyncStorageEngine.AuthorityInfo authority
|
||||
= mSyncStorageEngine.getAuthority(active.authorityId);
|
||||
final long durationInSeconds = (now - active.startTime) / 1000;
|
||||
pw.print("Active sync: ");
|
||||
pw.print(authority != null ? authority.account : "<no account>");
|
||||
pw.print(" ");
|
||||
pw.print(authority != null ? authority.authority : "<no account>");
|
||||
pw.print(", duration is ");
|
||||
pw.println(DateUtils.formatElapsedTime(durationInSeconds));
|
||||
} 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) {
|
||||
sb.append(" |_________________________________________________________|\n");
|
||||
private void dumpTimeSec(PrintWriter pw, long time) {
|
||||
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
|
||||
// start it, otherwise just get out.
|
||||
SyncOperation syncOperation;
|
||||
final Sync.Settings.QueryMap syncSettings = getSyncSettings();
|
||||
final ConnectivityManager connManager = (ConnectivityManager)
|
||||
mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
final boolean backgroundDataSetting = connManager.getBackgroundDataSetting();
|
||||
@@ -1488,9 +1483,9 @@ class SyncManager {
|
||||
final boolean force = syncOperation.extras.getBoolean(
|
||||
ContentResolver.SYNC_EXTRAS_FORCE, false);
|
||||
if (!force && (!backgroundDataSetting
|
||||
|| !syncSettings.getListenForNetworkTickles()
|
||||
|| !syncSettings.getSyncProviderAutomatically(
|
||||
syncOperation.authority))) {
|
||||
|| !mSyncStorageEngine.getListenForNetworkTickles()
|
||||
|| !mSyncStorageEngine.getSyncProviderAutomatically(
|
||||
null, syncOperation.authority))) {
|
||||
if (isLoggable) {
|
||||
Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation);
|
||||
}
|
||||
@@ -1616,7 +1611,7 @@ class SyncManager {
|
||||
if (isLoggable) {
|
||||
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
|
||||
downstreamActivity = 0;
|
||||
upstreamActivity = 0;
|
||||
@@ -1640,7 +1635,7 @@ class SyncManager {
|
||||
} catch (RemoteException e) {
|
||||
// we don't need to retry this in this case
|
||||
}
|
||||
historyMessage = History.MESG_CANCELED;
|
||||
historyMessage = SyncStorageEngine.MESG_CANCELED;
|
||||
downstreamActivity = 0;
|
||||
upstreamActivity = 0;
|
||||
}
|
||||
@@ -1675,14 +1670,22 @@ class SyncManager {
|
||||
* If SyncResult.error() is true then it is safe to call this.
|
||||
*/
|
||||
private int syncResultToErrorNumber(SyncResult syncResult) {
|
||||
if (syncResult.syncAlreadyInProgress) return History.ERROR_SYNC_ALREADY_IN_PROGRESS;
|
||||
if (syncResult.stats.numAuthExceptions > 0) return History.ERROR_AUTHENTICATION;
|
||||
if (syncResult.stats.numIoExceptions > 0) return History.ERROR_IO;
|
||||
if (syncResult.stats.numParseExceptions > 0) return History.ERROR_PARSE;
|
||||
if (syncResult.stats.numConflictDetectedExceptions > 0) return History.ERROR_CONFLICT;
|
||||
if (syncResult.tooManyDeletions) return History.ERROR_TOO_MANY_DELETIONS;
|
||||
if (syncResult.tooManyRetries) return History.ERROR_TOO_MANY_RETRIES;
|
||||
if (syncResult.databaseError) return History.ERROR_INTERNAL;
|
||||
if (syncResult.syncAlreadyInProgress)
|
||||
return SyncStorageEngine.ERROR_SYNC_ALREADY_IN_PROGRESS;
|
||||
if (syncResult.stats.numAuthExceptions > 0)
|
||||
return SyncStorageEngine.ERROR_AUTHENTICATION;
|
||||
if (syncResult.stats.numIoExceptions > 0)
|
||||
return SyncStorageEngine.ERROR_IO;
|
||||
if (syncResult.stats.numParseExceptions > 0)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1904,7 +1907,8 @@ class SyncManager {
|
||||
final int source = syncOperation.syncSource;
|
||||
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(
|
||||
syncOperation.account, syncOperation.authority, now, source);
|
||||
@@ -1912,7 +1916,8 @@ class SyncManager {
|
||||
|
||||
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
|
||||
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,
|
||||
downstreamActivity, upstreamActivity);
|
||||
@@ -1921,18 +1926,6 @@ class SyncManager {
|
||||
|
||||
static class SyncQueue {
|
||||
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;
|
||||
|
||||
@@ -1946,25 +1939,28 @@ class SyncManager {
|
||||
|
||||
public SyncQueue(SyncStorageEngine syncStorageEngine) {
|
||||
mSyncStorageEngine = syncStorageEngine;
|
||||
Cursor cursor = mSyncStorageEngine.getPendingSyncsCursor(COLUMNS);
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
add(cursorToOperation(cursor),
|
||||
true /* this is being added from the database */);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */);
|
||||
ArrayList<SyncStorageEngine.PendingOperation> ops
|
||||
= mSyncStorageEngine.getPendingOperations();
|
||||
final int N = ops.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
SyncStorageEngine.PendingOperation op = ops.get(i);
|
||||
SyncOperation syncOperation = new SyncOperation(
|
||||
op.account, op.syncSource, op.authority, op.extras, 0);
|
||||
syncOperation.pendingOperation = op;
|
||||
add(syncOperation, op);
|
||||
}
|
||||
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */);
|
||||
}
|
||||
|
||||
public boolean add(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) {
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase);
|
||||
private boolean add(SyncOperation operation,
|
||||
SyncStorageEngine.PendingOperation pop) {
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
|
||||
|
||||
// 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.
|
||||
@@ -1996,7 +1992,7 @@ class SyncManager {
|
||||
|
||||
if (existingOperation != null
|
||||
&& operation.earliestRunTime >= existingOperation.earliestRunTime) {
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase);
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2004,26 +2000,17 @@ class SyncManager {
|
||||
removeByKey(operationKey);
|
||||
}
|
||||
|
||||
if (operation.rowId == null) {
|
||||
byte[] extrasData = null;
|
||||
Parcel parcel = Parcel.obtain();
|
||||
try {
|
||||
operation.extras.writeToParcel(parcel, 0);
|
||||
extrasData = parcel.marshall();
|
||||
} finally {
|
||||
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) {
|
||||
operation.pendingOperation = pop;
|
||||
if (operation.pendingOperation == null) {
|
||||
pop = new SyncStorageEngine.PendingOperation(
|
||||
operation.account, operation.syncSource,
|
||||
operation.authority, operation.extras);
|
||||
pop = mSyncStorageEngine.insertIntoPending(pop);
|
||||
if (pop == null) {
|
||||
throw new IllegalStateException("error adding pending sync operation "
|
||||
+ operation);
|
||||
}
|
||||
operation.pendingOperation = pop;
|
||||
}
|
||||
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) {
|
||||
@@ -2033,7 +2020,7 @@ class SyncManager {
|
||||
}
|
||||
mOpsByKey.put(operationKey, operation);
|
||||
mOpsByWhen.add(operation);
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(!fromDatabase);
|
||||
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(pop == null);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2045,7 +2032,7 @@ class SyncManager {
|
||||
"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 "
|
||||
+ operationToRemove);
|
||||
}
|
||||
@@ -2065,7 +2052,7 @@ class SyncManager {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -2087,7 +2074,7 @@ class SyncManager {
|
||||
"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 "
|
||||
+ syncOperation);
|
||||
}
|
||||
@@ -2128,48 +2115,29 @@ class SyncManager {
|
||||
}
|
||||
|
||||
if (checkDatabase) {
|
||||
// check that the DB contains the same rows as the in-memory data structures
|
||||
Cursor cursor = mSyncStorageEngine.getPendingSyncsCursor(COLUMNS);
|
||||
try {
|
||||
if (mOpsByKey.size() != cursor.getCount()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
DatabaseUtils.dumpCursor(cursor, sb);
|
||||
final int N = mSyncStorageEngine.getPendingOperationCount();
|
||||
if (mOpsByKey.size() != N) {
|
||||
ArrayList<SyncStorageEngine.PendingOperation> ops
|
||||
= mSyncStorageEngine.getPendingOperations();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
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");
|
||||
dump(sb);
|
||||
throw new IllegalStateException("DB size mismatch: "
|
||||
+ mOpsByKey .size() + " != " + cursor.getCount() + "\n"
|
||||
+ sb.toString());
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
dump(sb);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
19
core/java/android/content/SyncStatusInfo.aidl
Normal file
19
core/java/android/content/SyncStatusInfo.aidl
Normal 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;
|
||||
108
core/java/android/content/SyncStatusInfo.java
Normal file
108
core/java/android/content/SyncStatusInfo.java
Normal 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
@@ -49,24 +49,34 @@ import java.util.HashMap;
|
||||
public class RemoteCallbackList<E extends IInterface> {
|
||||
/*package*/ HashMap<IBinder, Callback> mCallbacks
|
||||
= new HashMap<IBinder, Callback>();
|
||||
private IInterface[] mActiveBroadcast;
|
||||
private Object[] mActiveBroadcast;
|
||||
private boolean mKilled = false;
|
||||
|
||||
private final class Callback implements IBinder.DeathRecipient {
|
||||
final E mCallback;
|
||||
final Object mCookie;
|
||||
|
||||
Callback(E callback) {
|
||||
Callback(E callback, Object cookie) {
|
||||
mCallback = callback;
|
||||
mCookie = cookie;
|
||||
}
|
||||
|
||||
public void binderDied() {
|
||||
synchronized (mCallbacks) {
|
||||
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
|
||||
* 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
|
||||
* an object given from a client, so that clients can't crash the
|
||||
* 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.
|
||||
* 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 #onCallbackDied
|
||||
*/
|
||||
public boolean register(E callback) {
|
||||
public boolean register(E callback, Object cookie) {
|
||||
synchronized (mCallbacks) {
|
||||
if (mKilled) {
|
||||
return false;
|
||||
}
|
||||
IBinder binder = callback.asBinder();
|
||||
try {
|
||||
Callback cb = new Callback(callback);
|
||||
Callback cb = new Callback(callback, cookie);
|
||||
binder.linkToDeath(cb, 0);
|
||||
mCallbacks.put(binder, cb);
|
||||
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.
|
||||
* 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
|
||||
* 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
|
||||
* IBinder to see if it is the same object.
|
||||
* @param cookie The cookie object original provided to
|
||||
* {@link #register(E, Object)}.
|
||||
*
|
||||
* @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) {
|
||||
return 0;
|
||||
}
|
||||
IInterface[] active = mActiveBroadcast;
|
||||
Object[] active = mActiveBroadcast;
|
||||
if (active == null || active.length < N) {
|
||||
mActiveBroadcast = active = new IInterface[N];
|
||||
mActiveBroadcast = active = new Object[N];
|
||||
}
|
||||
int i=0;
|
||||
for (Callback cb : mCallbacks.values()) {
|
||||
active[i++] = cb.mCallback;
|
||||
active[i++] = cb;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
@@ -237,7 +260,17 @@ public class RemoteCallbackList<E extends IInterface> {
|
||||
* @see #beginBroadcast
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public void finishBroadcast() {
|
||||
IInterface[] active = mActiveBroadcast;
|
||||
Object[] active = mActiveBroadcast;
|
||||
if (active != null) {
|
||||
final int N = active.length;
|
||||
for (int i=0; i<N; i++) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
153
core/java/com/android/internal/os/AtomicFile.java
Normal file
153
core/java/com/android/internal/os/AtomicFile.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1012,9 +1012,6 @@
|
||||
android:excludeFromRecents="true">
|
||||
</activity>
|
||||
|
||||
<provider android:name=".content.SyncProvider"
|
||||
android:authorities="sync" android:multiprocess="false" />
|
||||
|
||||
<service android:name="com.android.server.LoadAverageService"
|
||||
android:exported="true" />
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import android.util.Log;
|
||||
import android.util.Config;
|
||||
import android.util.EventLog;
|
||||
import android.app.IntentService;
|
||||
import android.provider.Sync;
|
||||
import android.provider.SubscribedFeeds;
|
||||
import android.provider.SyncConstValue;
|
||||
import android.database.Cursor;
|
||||
@@ -17,7 +16,7 @@ import android.database.sqlite.SQLiteFullException;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.net.Uri;
|
||||
|
||||
@@ -105,10 +104,6 @@ public class SubscribedFeedsIntentService extends IntentService {
|
||||
|
||||
private void handleTickle(Context context, String account, String feed) {
|
||||
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 + "= ? "
|
||||
+ "and " + SubscribedFeeds.Feeds.FEED + "= ?";
|
||||
try {
|
||||
@@ -124,9 +119,14 @@ public class SubscribedFeedsIntentService extends IntentService {
|
||||
String authority = c.getString(c.getColumnIndexOrThrow(
|
||||
SubscribedFeeds.Feeds.AUTHORITY));
|
||||
EventLog.writeEvent(LOG_TICKLE, authority);
|
||||
if (!syncSettings.getSyncProviderAutomatically(authority)) {
|
||||
Log.d(TAG, "supressing tickle since provider " + authority
|
||||
+ " is configured to not sync automatically");
|
||||
try {
|
||||
if (!ContentResolver.getContentService()
|
||||
.getSyncProviderAutomatically(authority)) {
|
||||
Log.d(TAG, "supressing tickle since provider " + authority
|
||||
+ " is configured to not sync automatically");
|
||||
continue;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
continue;
|
||||
}
|
||||
Uri uri = Uri.parse("content://" + authority);
|
||||
@@ -137,7 +137,6 @@ public class SubscribedFeedsIntentService extends IntentService {
|
||||
}
|
||||
} finally {
|
||||
if (c != null) c.deactivate();
|
||||
syncSettings.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,8 +76,6 @@ android.content.ContentQueryMap
|
||||
android.content.ContentQueryMap$1
|
||||
android.content.ContentResolver
|
||||
android.content.ContentResolver$CursorWrapperInner
|
||||
android.content.ContentServiceNative
|
||||
android.content.ContentServiceProxy
|
||||
android.content.ContentValues
|
||||
android.content.Context
|
||||
android.content.ContextWrapper
|
||||
@@ -85,6 +83,8 @@ android.content.DialogInterface
|
||||
android.content.DialogInterface$OnCancelListener
|
||||
android.content.DialogInterface$OnDismissListener
|
||||
android.content.IContentProvider
|
||||
android.content.IContentService
|
||||
android.content.IContentService$Stub
|
||||
android.content.Intent
|
||||
android.content.Intent$1
|
||||
android.content.IntentFilter
|
||||
|
||||
@@ -35,7 +35,6 @@ import android.os.Message;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemProperties;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Sync;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
@@ -18,12 +18,10 @@ package android.test;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.ContentValues;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Sync;
|
||||
import android.net.Uri;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* If you would like to test sync a single provider with an
|
||||
@@ -75,11 +73,11 @@ public class SyncBaseInstrumentation extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
protected void cancelSyncsandDisableAutoSync() {
|
||||
Sync.Settings.QueryMap mSyncSettings =
|
||||
new Sync.Settings.QueryMap(mContentResolver, true, null);
|
||||
mSyncSettings.setListenForNetworkTickles(false);
|
||||
try {
|
||||
ContentResolver.getContentService().setListenForNetworkTickles(false);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
mContentResolver.cancelSync(null);
|
||||
mSyncSettings.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,34 +86,11 @@ public class SyncBaseInstrumentation extends InstrumentationTestCase {
|
||||
* @return
|
||||
*/
|
||||
private boolean isSyncActive(String account, String authority) {
|
||||
Sync.Pending.QueryMap pendingQueryMap = null;
|
||||
Sync.Active.QueryMap activeQueryMap = null;
|
||||
try {
|
||||
pendingQueryMap = new Sync.Pending.QueryMap(mContentResolver, false, null);
|
||||
activeQueryMap = new Sync.Active.QueryMap(mContentResolver, false, null);
|
||||
|
||||
if (pendingQueryMap.isPending(account, authority)) {
|
||||
return true;
|
||||
}
|
||||
if (isActiveInActiveQueryMap(activeQueryMap, account, authority)) {
|
||||
return true;
|
||||
}
|
||||
return ContentResolver.getContentService().isSyncActive(account,
|
||||
authority);
|
||||
} catch (RemoteException e) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.test.AndroidTestCase;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.mock.MockContext;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.provider.Sync;
|
||||
|
||||
public class SyncStorageEngineTest extends AndroidTestCase {
|
||||
|
||||
@@ -39,7 +38,7 @@ public class SyncStorageEngineTest extends AndroidTestCase {
|
||||
|
||||
long time0 = 1000;
|
||||
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;
|
||||
engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.layoutlib.bridge;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentServiceNative;
|
||||
import android.content.Context;
|
||||
import android.content.IContentProvider;
|
||||
import android.database.ContentObserver;
|
||||
@@ -51,9 +50,6 @@ public class BridgeContentResolver extends ContentResolver {
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void registerContentObserver(Uri uri, boolean notifyForDescendents,
|
||||
@@ -63,9 +59,6 @@ public class BridgeContentResolver extends ContentResolver {
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void unregisterContentObserver(ContentObserver observer) {
|
||||
@@ -74,9 +67,6 @@ public class BridgeContentResolver extends ContentResolver {
|
||||
|
||||
/**
|
||||
* 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
|
||||
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.
|
||||
* <p/>
|
||||
* The super implementation accesses the {@link ContentServiceNative#getDefault()}
|
||||
* which returns null and would make the call crash. Instead we do nothing.
|
||||
*/
|
||||
@Override
|
||||
public void startSync(Uri uri, Bundle extras) {
|
||||
@@ -96,9 +83,6 @@ public class BridgeContentResolver extends ContentResolver {
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void cancelSync(Uri uri) {
|
||||
|
||||
Reference in New Issue
Block a user