Merge change 777 into donut
* changes: Rewrite SyncStorageEngine to use flat files and in-memory data structures.
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