Add 1-day sync stats in syncmanager dumpsys.
The stats currently shown in sync manager dumpsys is the total *since wipe* so
they're usually pretty much useless.
So let's add "today" and "yesterday" stats too.
- Also add "# failures" and "# cancels".
- Also split up SERVER to OTHER and FEED (i.e. subscribedfeeds).
Bug: 76035392
Test: Boot, run some sync, then do "cmd content reset-today-stats"
- Also tested with actually setting the clock to 23:59 and wait
- Check the result with "dumpsys content".
Sample output:
=======================================================================
Authority Syncable Enabled Stats Loc Poll Per Feed User Othr Tot Fail Can Time Last Sync Backoff
------------------------------------------------------------------------------------------------------------------------------------------------------------------
com.android.calendar 1 true Total 0 0 0 1 0 7 8 0 2 8s FEED SUCCESS
Today 1 5 6 7s 2018-04-12 15:21:24
Yestr 0s
com.android.chrome 0 false Total 0 0 0 0 0 0 0 0 0 0s
Today 0s
Change-Id: Id0ea42435a9f759e47d4b9490292759270f8e9a5
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index c500116..dc17666 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -183,4 +183,6 @@
void putCache(in String packageName, in Uri key, in Bundle value, int userId);
Bundle getCache(in String packageName, in Uri key, int userId);
+
+ void resetTodayStats();
}
diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java
index abf9cc9..ded11cfd 100644
--- a/core/java/android/content/SyncStatusInfo.java
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -21,23 +21,97 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
/** @hide */
public class SyncStatusInfo implements Parcelable {
private static final String TAG = "Sync";
- static final int VERSION = 4;
+ static final int VERSION = 5;
private static final int MAX_EVENT_COUNT = 10;
public final int authorityId;
- public long totalElapsedTime;
- public int numSyncs;
- public int numSourcePoll;
- public int numSourceServer;
- public int numSourceLocal;
- public int numSourceUser;
- public int numSourcePeriodic;
+
+ /**
+ * # of syncs for each sync source, etc.
+ */
+ public static class Stats {
+ public long totalElapsedTime;
+ public int numSyncs;
+ public int numSourcePoll;
+ public int numSourceOther;
+ public int numSourceLocal;
+ public int numSourceUser;
+ public int numSourcePeriodic;
+ public int numSourceFeed;
+ public int numFailures;
+ public int numCancels;
+
+ /** Copy all the stats to another instance. */
+ public void copyTo(Stats to) {
+ to.totalElapsedTime = totalElapsedTime;
+ to.numSyncs = numSyncs;
+ to.numSourcePoll = numSourcePoll;
+ to.numSourceOther = numSourceOther;
+ to.numSourceLocal = numSourceLocal;
+ to.numSourceUser = numSourceUser;
+ to.numSourcePeriodic = numSourcePeriodic;
+ to.numSourceFeed = numSourceFeed;
+ to.numFailures = numFailures;
+ to.numCancels = numCancels;
+ }
+
+ /** Clear all the stats. */
+ public void clear() {
+ totalElapsedTime = 0;
+ numSyncs = 0;
+ numSourcePoll = 0;
+ numSourceOther = 0;
+ numSourceLocal = 0;
+ numSourceUser = 0;
+ numSourcePeriodic = 0;
+ numSourceFeed = 0;
+ numFailures = 0;
+ numCancels = 0;
+ }
+
+ /** Write all the stats to a parcel. */
+ public void writeToParcel(Parcel parcel) {
+ parcel.writeLong(totalElapsedTime);
+ parcel.writeInt(numSyncs);
+ parcel.writeInt(numSourcePoll);
+ parcel.writeInt(numSourceOther);
+ parcel.writeInt(numSourceLocal);
+ parcel.writeInt(numSourceUser);
+ parcel.writeInt(numSourcePeriodic);
+ parcel.writeInt(numSourceFeed);
+ parcel.writeInt(numFailures);
+ parcel.writeInt(numCancels);
+ }
+
+ /** Read all the stats from a parcel. */
+ public void readFromParcel(Parcel parcel) {
+ totalElapsedTime = parcel.readLong();
+ numSyncs = parcel.readInt();
+ numSourcePoll = parcel.readInt();
+ numSourceOther = parcel.readInt();
+ numSourceLocal = parcel.readInt();
+ numSourceUser = parcel.readInt();
+ numSourcePeriodic = parcel.readInt();
+ numSourceFeed = parcel.readInt();
+ numFailures = parcel.readInt();
+ numCancels = parcel.readInt();
+ }
+ }
+
+ public long lastTodayResetTime;
+
+ public final Stats totalStats = new Stats();
+ public final Stats todayStats = new Stats();
+ public final Stats yesterdayStats = new Stats();
+
public long lastSuccessTime;
public int lastSuccessSource;
public long lastFailureTime;
@@ -75,12 +149,15 @@
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);
+
+ // Note we can't use Stats.writeToParcel() here; see the below constructor for the reason.
+ parcel.writeLong(totalStats.totalElapsedTime);
+ parcel.writeInt(totalStats.numSyncs);
+ parcel.writeInt(totalStats.numSourcePoll);
+ parcel.writeInt(totalStats.numSourceOther);
+ parcel.writeInt(totalStats.numSourceLocal);
+ parcel.writeInt(totalStats.numSourceUser);
+
parcel.writeLong(lastSuccessTime);
parcel.writeInt(lastSuccessSource);
parcel.writeLong(lastFailureTime);
@@ -102,7 +179,18 @@
parcel.writeLong(mLastEventTimes.get(i));
parcel.writeString(mLastEvents.get(i));
}
- parcel.writeInt(numSourcePeriodic);
+ // Version 4
+ parcel.writeInt(totalStats.numSourcePeriodic);
+
+ // Version 5
+ parcel.writeInt(totalStats.numSourceFeed);
+ parcel.writeInt(totalStats.numFailures);
+ parcel.writeInt(totalStats.numCancels);
+
+ parcel.writeLong(lastTodayResetTime);
+
+ todayStats.writeToParcel(parcel);
+ yesterdayStats.writeToParcel(parcel);
}
public SyncStatusInfo(Parcel parcel) {
@@ -111,12 +199,15 @@
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();
+
+ // Note we can't use Stats.writeToParcel() here because the data is persisted and we need
+ // to be able to read from the old format too.
+ totalStats.totalElapsedTime = parcel.readLong();
+ totalStats.numSyncs = parcel.readInt();
+ totalStats.numSourcePoll = parcel.readInt();
+ totalStats.numSourceOther = parcel.readInt();
+ totalStats.numSourceLocal = parcel.readInt();
+ totalStats.numSourceUser = parcel.readInt();
lastSuccessTime = parcel.readLong();
lastSuccessSource = parcel.readInt();
lastFailureTime = parcel.readLong();
@@ -149,25 +240,37 @@
}
if (version < 4) {
// Before version 4, numSourcePeriodic wasn't persisted.
- numSourcePeriodic = numSyncs - numSourceLocal - numSourcePoll - numSourceServer
- - numSourceUser;
- if (numSourcePeriodic < 0) { // Sanity check.
- numSourcePeriodic = 0;
+ totalStats.numSourcePeriodic =
+ totalStats.numSyncs - totalStats.numSourceLocal - totalStats.numSourcePoll
+ - totalStats.numSourceOther
+ - totalStats.numSourceUser;
+ if (totalStats.numSourcePeriodic < 0) { // Sanity check.
+ totalStats.numSourcePeriodic = 0;
}
} else {
- numSourcePeriodic = parcel.readInt();
+ totalStats.numSourcePeriodic = parcel.readInt();
+ }
+ if (version >= 5) {
+ totalStats.numSourceFeed = parcel.readInt();
+ totalStats.numFailures = parcel.readInt();
+ totalStats.numCancels = parcel.readInt();
+
+ lastTodayResetTime = parcel.readLong();
+
+ todayStats.readFromParcel(parcel);
+ yesterdayStats.readFromParcel(parcel);
}
}
public SyncStatusInfo(SyncStatusInfo other) {
authorityId = other.authorityId;
- totalElapsedTime = other.totalElapsedTime;
- numSyncs = other.numSyncs;
- numSourcePoll = other.numSourcePoll;
- numSourceServer = other.numSourceServer;
- numSourceLocal = other.numSourceLocal;
- numSourceUser = other.numSourceUser;
- numSourcePeriodic = other.numSourcePeriodic;
+
+ other.totalStats.copyTo(totalStats);
+ other.todayStats.copyTo(todayStats);
+ other.yesterdayStats.copyTo(yesterdayStats);
+
+ lastTodayResetTime = other.lastTodayResetTime;
+
lastSuccessTime = other.lastSuccessTime;
lastSuccessSource = other.lastSuccessSource;
lastFailureTime = other.lastFailureTime;
@@ -251,4 +354,41 @@
}
}
}
+
+ /**
+ * If the last reset was not not today, move today's stats to yesterday's and clear today's.
+ */
+ public void maybeResetTodayStats(boolean clockValid, boolean force) {
+ final long now = System.currentTimeMillis();
+
+ if (!force) {
+ // Last reset was the same day, nothing to do.
+ if (areSameDates(now, lastTodayResetTime)) {
+ return;
+ }
+
+ // Hack -- on devices with no RTC, until the NTP kicks in, the device won't have the
+ // correct time. So if the time goes back, don't reset, unless we're sure the current
+ // time is correct.
+ if (now < lastTodayResetTime && !clockValid) {
+ return;
+ }
+ }
+
+ lastTodayResetTime = now;
+
+ todayStats.copyTo(yesterdayStats);
+ todayStats.clear();
+ }
+
+ private static boolean areSameDates(long time1, long time2) {
+ final Calendar c1 = new GregorianCalendar();
+ final Calendar c2 = new GregorianCalendar();
+
+ c1.setTimeInMillis(time1);
+ c2.setTimeInMillis(time2);
+
+ return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
+ && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index e3d0bdd..e840a29 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -51,6 +51,8 @@
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -1577,4 +1579,32 @@
}
}
}
+
+ private void enforceShell(String method) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
+ throw new SecurityException("Non-shell user attempted to call " + method);
+ }
+ }
+
+ @Override
+ public void resetTodayStats() {
+ enforceShell("resetTodayStats");
+
+ if (mSyncManager != null) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSyncManager.resetTodayStats();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new ContentShellCommand(this)).exec(this, in, out, err, args, callback, resultReceiver);
+ }
}
diff --git a/services/core/java/com/android/server/content/ContentShellCommand.java b/services/core/java/com/android/server/content/ContentShellCommand.java
new file mode 100644
index 0000000..6c2991f
--- /dev/null
+++ b/services/core/java/com/android/server/content/ContentShellCommand.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.server.content;
+
+import android.content.IContentService;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+public class ContentShellCommand extends ShellCommand {
+ final IContentService mInterface;
+
+ ContentShellCommand(IContentService service) {
+ mInterface = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch(cmd) {
+ case "reset-today-stats":
+ return runResetTodayStats();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int runResetTodayStats() throws RemoteException {
+ mInterface.resetTodayStats();
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Content service commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" reset-today-stats");
+ pw.println(" Reset 1-day sync stats.");
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index a312fe1..decae18 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -48,6 +48,7 @@
import android.content.SyncInfo;
import android.content.SyncResult;
import android.content.SyncStatusInfo;
+import android.content.SyncStatusInfo.Stats;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -95,6 +96,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.function.QuadConsumer;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -122,6 +124,7 @@
import java.util.Objects;
import java.util.Random;
import java.util.Set;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -242,7 +245,7 @@
/** Track whether the device has already been provisioned. */
private volatile boolean mProvisioned;
- protected SyncAdaptersCache mSyncAdapters;
+ protected final SyncAdaptersCache mSyncAdapters;
private final Random mRand;
@@ -423,6 +426,17 @@
}
};
+ private final BroadcastReceiver mOtherIntentsReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
+ mSyncStorageEngine.setClockValid();
+ return;
+ }
+ }
+ };
+
private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -631,6 +645,9 @@
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
+ context.registerReceiver(mOtherIntentsReceiver, intentFilter);
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -947,9 +964,13 @@
} else if (requestedAuthority == null) {
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 = SyncStorageEngine.SOURCE_SERVER;
+ if (extras.containsKey("feed")) {
+ source = SyncStorageEngine.SOURCE_FEED;
+ } else{
+ // This isn't strictly server, since arbitrary callers can (and do) request
+ // a non-forced two-way sync on a specific url.
+ source = SyncStorageEngine.SOURCE_OTHER;
+ }
}
for (AccountAndUser account : accounts) {
@@ -2134,6 +2155,7 @@
pw.print("Memory low: "); pw.println(mStorageIsLow);
pw.print("Device idle: "); pw.println(mDeviceIsIdle);
pw.print("Reported active: "); pw.println(mReportedSyncActive);
+ pw.print("Clock valid: "); pw.println(mSyncStorageEngine.isClockValid());
final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
@@ -2181,26 +2203,35 @@
final ArrayList<Pair<EndPoint, SyncStatusInfo>> statuses = new ArrayList<>();
+ mSyncStorageEngine.resetTodayStats(/* force=*/ false);
+
for (AccountAndUser account : accounts) {
pw.printf("Account %s u%d %s\n",
account.account.name, account.userId, account.account.type);
pw.println("=======================================================================");
- final PrintTable table = new PrintTable(13);
+ final PrintTable table = new PrintTable(16);
table.set(0, 0,
"Authority", // 0
"Syncable", // 1
"Enabled", // 2
- "Delay", // 3
- "Loc", // 4
- "Poll", // 5
- "Per", // 6
- "Serv", // 7
- "User", // 8
- "Tot", // 9
- "Time", // 10
- "Last Sync", // 11
- "Backoff" // 12
+
+ "Stats", // 3 "Total", "Today" or "Yesterday".
+
+ "Loc", // 4 # of syncs with local sources. (including failures/cancels. )
+ "Poll", // 5 "poll" syncs.
+ "Per", // 6 Periodic syncs.
+ "Feed", // 7 Syncs with a "feed" extra. (subscribedfeeds?)
+ "User", // 8 User-initiated
+ "Othr", // 9 Other sources.
+
+ "Tot", // 10 Total syncs (including failures / cancels)
+ "Fail", // 11 (Failure)
+ "Can", // 12 (Cancel)
+
+ "Time", // 13 Total time
+ "Last Sync", // 14
+ "Backoff" // 15
);
final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
@@ -2234,37 +2265,50 @@
}
table.set(row, 0, authority, settings.syncable, settings.enabled);
- sb.setLength(0);
- table.set(row, 4,
- status.numSourceLocal,
- status.numSourcePoll,
- status.numSourcePeriodic,
- status.numSourceServer,
- status.numSourceUser,
- status.numSyncs,
- formatDurationHMS(sb, status.totalElapsedTime));
+ QuadConsumer<String, Stats, Function<Integer, String>, Integer> c =
+ (label, stats, filter, r) -> {
+ sb.setLength(0);
+ table.set(r, 3,
+ label,
+ filter.apply(stats.numSourceLocal),
+ filter.apply(stats.numSourcePoll),
+ filter.apply(stats.numSourcePeriodic),
+ filter.apply(stats.numSourceFeed),
+ filter.apply(stats.numSourceUser),
+ filter.apply(stats.numSourceOther),
+ filter.apply(stats.numSyncs),
+ filter.apply(stats.numFailures),
+ filter.apply(stats.numCancels),
+ formatDurationHMS(sb, stats.totalElapsedTime));
+ };
+ c.accept("Total", status.totalStats, (i) -> Integer.toString(i), row);
+ c.accept("Today", status.todayStats, this::zeroToEmpty, row + 1);
+ c.accept("Yestr", status.yesterdayStats, this::zeroToEmpty, row + 2);
+
+ final int LAST_SYNC = 14;
+ final int BACKOFF = LAST_SYNC + 1;
int row1 = row;
if (settings.delayUntil > now) {
- table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000);
+ table.set(row1++, BACKOFF, "D: " + (settings.delayUntil - now) / 1000);
if (settings.backoffTime > now) {
- table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000);
- table.set(row1++, 12, settings.backoffDelay / 1000);
+ table.set(row1++, BACKOFF, "B: " + (settings.backoffTime - now) / 1000);
+ table.set(row1++, BACKOFF, settings.backoffDelay / 1000);
}
}
row1 = row;
if (status.lastSuccessTime != 0) {
- table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource]
+ table.set(row1++, LAST_SYNC, SyncStorageEngine.SOURCES[status.lastSuccessSource]
+ " " + "SUCCESS");
- table.set(row1++, 11, formatTime(status.lastSuccessTime));
+ table.set(row1++, LAST_SYNC, formatTime(status.lastSuccessTime));
}
if (status.lastFailureTime != 0) {
- table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource]
+ table.set(row1++, LAST_SYNC, SyncStorageEngine.SOURCES[status.lastFailureSource]
+ " " + "FAILURE");
- table.set(row1++, 11, formatTime(status.lastFailureTime));
+ table.set(row1++, LAST_SYNC, formatTime(status.lastFailureTime));
//noinspection UnusedAssignment
- table.set(row1++, 11, status.lastFailureMesg);
+ table.set(row1++, LAST_SYNC, status.lastFailureMesg);
}
}
table.writeTo(pw);
@@ -2274,6 +2318,7 @@
pw.println();
pw.println("Per Adapter History");
+ pw.println("(SERVER is now split up to FEED and OTHER)");
for (int i = 0; i < statuses.size(); i++) {
final Pair<EndPoint, SyncStatusInfo> event = statuses.get(i);
@@ -2299,6 +2344,10 @@
}
}
+ private String zeroToEmpty(int value) {
+ return (value != 0) ? Integer.toString(value) : "";
+ }
+
private void dumpTimeSec(PrintWriter pw, long time) {
pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
pw.print('s');
@@ -2459,6 +2508,7 @@
pw.println();
pw.println("Recent Sync History");
+ pw.println("(SERVER is now split up to FEED and OTHER)");
final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n";
final Map<String, Long> lastTimeMap = Maps.newHashMap();
final PackageManager pm = mContext.getPackageManager();
@@ -2525,6 +2575,7 @@
}
pw.println();
pw.println("Recent Sync History Extras");
+ pw.println("(SERVER is now split up to FEED and OTHER)");
for (int i = 0; i < N; i++) {
final SyncStorageEngine.SyncHistoryItem item = items.get(i);
final Bundle extras = item.extras;
@@ -3104,6 +3155,10 @@
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (isLoggable) Slog.v(TAG, op.toString());
+ // At this point, we know the device has been connected to the server, so
+ // assume the clock is correct.
+ mSyncStorageEngine.setClockValid();
+
mSyncJobService.markSyncStarted(op.jobId);
if (mStorageIsLow) {
@@ -4015,4 +4070,8 @@
Slog.wtf(TAG, message);
mLogger.log("WTF: ", message);
}
+
+ public void resetTodayStats() {
+ mSyncStorageEngine.resetTodayStats(/*force=*/ true);
+ }
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 8dd229c..f54a9a0 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -108,11 +108,12 @@
/** Enum value for a sync stop event. */
public static final int EVENT_STOP = 1;
- /** Enum value for a server-initiated sync. */
- public static final int SOURCE_SERVER = 0;
+ /** Enum value for a sync with other sources. */
+ public static final int SOURCE_OTHER = 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;
@@ -122,16 +123,18 @@
/** Enum value for a periodic sync. */
public static final int SOURCE_PERIODIC = 4;
+ /** Enum a sync with a "feed" extra */
+ public static final int SOURCE_FEED = 5;
+
public static final long NOT_IN_BACKOFF_MODE = -1;
- // TODO: i18n -- grab these out of resources.
/** String names for the sync source types. */
- public static final String[] SOURCES = { "SERVER",
+ public static final String[] SOURCES = { "OTHER",
"LOCAL",
"POLL",
"USER",
"PERIODIC",
- "SERVICE"};
+ "FEED"};
// The MESG column will contain one of these or one of the Error types.
public static final String MESG_SUCCESS = "success";
@@ -153,6 +156,8 @@
private static HashMap<String, String> sAuthorityRenames;
private static PeriodicSyncAddedListener mPeriodicSyncAddedListener;
+ private volatile boolean mIsClockValid;
+
static {
sAuthorityRenames = new HashMap<String, String>();
sAuthorityRenames.put("contacts", "com.android.contacts");
@@ -1174,23 +1179,36 @@
SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
- status.numSyncs++;
- status.totalElapsedTime += elapsedTime;
+ status.maybeResetTodayStats(isClockValid(), /*force=*/ false);
+
+ status.totalStats.numSyncs++;
+ status.todayStats.numSyncs++;
+ status.totalStats.totalElapsedTime += elapsedTime;
+ status.todayStats.totalElapsedTime += elapsedTime;
switch (item.source) {
case SOURCE_LOCAL:
- status.numSourceLocal++;
+ status.totalStats.numSourceLocal++;
+ status.todayStats.numSourceLocal++;
break;
case SOURCE_POLL:
- status.numSourcePoll++;
+ status.totalStats.numSourcePoll++;
+ status.todayStats.numSourcePoll++;
break;
case SOURCE_USER:
- status.numSourceUser++;
+ status.totalStats.numSourceUser++;
+ status.todayStats.numSourceUser++;
break;
- case SOURCE_SERVER:
- status.numSourceServer++;
+ case SOURCE_OTHER:
+ status.totalStats.numSourceOther++;
+ status.todayStats.numSourceOther++;
break;
case SOURCE_PERIODIC:
- status.numSourcePeriodic++;
+ status.totalStats.numSourcePeriodic++;
+ status.todayStats.numSourcePeriodic++;
+ break;
+ case SOURCE_FEED:
+ status.totalStats.numSourceFeed++;
+ status.todayStats.numSourceFeed++;
break;
}
@@ -1225,6 +1243,9 @@
if (status.lastFailureTime == 0) {
writeStatusNow = true;
}
+ status.totalStats.numFailures++;
+ status.todayStats.numFailures++;
+
status.lastFailureTime = lastSyncTime;
status.lastFailureSource = item.source;
status.lastFailureMesg = resultMessage;
@@ -1233,6 +1254,11 @@
}
ds.failureCount++;
ds.failureTime += elapsedTime;
+ } else {
+ // Cancel
+ status.totalStats.numCancels++;
+ status.todayStats.numCancels++;
+ writeStatusNow = true;
}
final StringBuilder event = new StringBuilder();
event.append("" + resultMessage + " Source=" + SyncStorageEngine.SOURCES[item.source]
@@ -1969,9 +1995,8 @@
}
/**
- * Load sync engine state from the old syncmanager database, and then
- * erase it. Note that we don't deal with pending operations, active
- * sync, or history.
+ * TODO Remove it. It's super old code that was used to migrate the information from a sqlite
+ * database that we used a long time ago, and is no longer relevant.
*/
private void readAndDeleteLegacyAccountInfoLocked() {
// Look for old database to initialize from.
@@ -2049,13 +2074,13 @@
st = new SyncStatusInfo(authority.ident);
mSyncStatus.put(authority.ident, st);
}
- st.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
- st.numSyncs = getIntColumn(c, "numSyncs");
- st.numSourceLocal = getIntColumn(c, "numSourceLocal");
- st.numSourcePoll = getIntColumn(c, "numSourcePoll");
- st.numSourceServer = getIntColumn(c, "numSourceServer");
- st.numSourceUser = getIntColumn(c, "numSourceUser");
- st.numSourcePeriodic = 0;
+ st.totalStats.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
+ st.totalStats.numSyncs = getIntColumn(c, "numSyncs");
+ st.totalStats.numSourceLocal = getIntColumn(c, "numSourceLocal");
+ st.totalStats.numSourcePoll = getIntColumn(c, "numSourcePoll");
+ st.totalStats.numSourceOther = getIntColumn(c, "numSourceServer");
+ st.totalStats.numSourceUser = getIntColumn(c, "numSourceUser");
+ st.totalStats.numSourcePeriodic = 0;
st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
st.lastFailureSource = getIntColumn(c, "lastFailureSource");
@@ -2296,4 +2321,29 @@
public void queueBackup() {
BackupManager.dataChanged("android");
}
+
+ public void setClockValid() {
+ if (!mIsClockValid) {
+ mIsClockValid = true;
+ Slog.w(TAG, "Clock is valid now.");
+ }
+ }
+
+ public boolean isClockValid() {
+ return mIsClockValid;
+ }
+
+ public void resetTodayStats(boolean force) {
+ if (force) {
+ Log.w(TAG, "Force resetting today stats.");
+ }
+ synchronized (mAuthorities) {
+ final int N = mSyncStatus.size();
+ for (int i = 0; i < N; i++) {
+ SyncStatusInfo cur = mSyncStatus.valueAt(i);
+ cur.maybeResetTodayStats(isClockValid(), force);
+ }
+ writeStatusLocked();
+ }
+ }
}