Loading core/java/android/content/IContentService.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -183,4 +183,6 @@ interface IContentService { 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(); } core/java/android/content/SyncStatusInfo.java +173 −33 Original line number Diff line number Diff line Loading @@ -21,23 +21,97 @@ import android.os.Parcelable; 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; /** * # of syncs for each sync source, etc. */ public static class Stats { public long totalElapsedTime; public int numSyncs; public int numSourcePoll; public int numSourceServer; 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; Loading Loading @@ -75,12 +149,15 @@ public class SyncStatusInfo implements Parcelable { 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); Loading @@ -102,7 +179,18 @@ public class SyncStatusInfo implements Parcelable { 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) { Loading @@ -111,12 +199,15 @@ public class SyncStatusInfo implements Parcelable { 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(); Loading Loading @@ -149,25 +240,37 @@ public class SyncStatusInfo implements Parcelable { } 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; Loading Loading @@ -251,4 +354,41 @@ public class SyncStatusInfo implements Parcelable { } } } /** * 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 services/core/java/com/android/server/content/ContentService.java +30 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,8 @@ import android.os.IBinder; 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; Loading Loading @@ -1577,4 +1579,32 @@ public final class ContentService extends IContentService.Stub { } } } 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); } } services/core/java/com/android/server/content/ContentShellCommand.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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(); } } services/core/java/com/android/server/content/SyncManager.java +91 −32 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.content.SyncAdaptersCache; 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; Loading Loading @@ -95,6 +96,7 @@ import com.android.internal.notification.SystemNotificationChannels; 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; Loading Loading @@ -122,6 +124,7 @@ import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; /** Loading Loading @@ -242,7 +245,7 @@ public class SyncManager { /** Track whether the device has already been provisioned. */ private volatile boolean mProvisioned; protected SyncAdaptersCache mSyncAdapters; protected final SyncAdaptersCache mSyncAdapters; private final Random mRand; Loading Loading @@ -423,6 +426,17 @@ public class SyncManager { } }; 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) { Loading Loading @@ -631,6 +645,9 @@ public class SyncManager { 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); Loading Loading @@ -946,10 +963,14 @@ public class SyncManager { source = SyncStorageEngine.SOURCE_USER; } else if (requestedAuthority == null) { source = SyncStorageEngine.SOURCE_POLL; } else { 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_SERVER; source = SyncStorageEngine.SOURCE_OTHER; } } for (AccountAndUser account : accounts) { Loading Loading @@ -2134,6 +2155,7 @@ public class SyncManager { 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(); Loading Loading @@ -2181,26 +2203,35 @@ public class SyncManager { 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 = Loading Loading @@ -2234,37 +2265,50 @@ public class SyncManager { } table.set(row, 0, authority, settings.syncable, settings.enabled); QuadConsumer<String, Stats, Function<Integer, String>, Integer> c = (label, stats, filter, r) -> { sb.setLength(0); table.set(row, 4, status.numSourceLocal, status.numSourcePoll, status.numSourcePeriodic, status.numSourceServer, status.numSourceUser, status.numSyncs, formatDurationHMS(sb, status.totalElapsedTime)); 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); Loading @@ -2274,6 +2318,7 @@ public class SyncManager { 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); Loading @@ -2299,6 +2344,10 @@ public class SyncManager { } } 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'); Loading Loading @@ -2459,6 +2508,7 @@ public class SyncManager { 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(); Loading Loading @@ -2525,6 +2575,7 @@ public class SyncManager { } 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; Loading Loading @@ -3104,6 +3155,10 @@ public class SyncManager { 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) { Loading Loading @@ -4015,4 +4070,8 @@ public class SyncManager { Slog.wtf(TAG, message); mLogger.log("WTF: ", message); } public void resetTodayStats() { mSyncStorageEngine.resetTodayStats(/*force=*/ true); } } Loading
core/java/android/content/IContentService.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -183,4 +183,6 @@ interface IContentService { 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(); }
core/java/android/content/SyncStatusInfo.java +173 −33 Original line number Diff line number Diff line Loading @@ -21,23 +21,97 @@ import android.os.Parcelable; 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; /** * # of syncs for each sync source, etc. */ public static class Stats { public long totalElapsedTime; public int numSyncs; public int numSourcePoll; public int numSourceServer; 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; Loading Loading @@ -75,12 +149,15 @@ public class SyncStatusInfo implements Parcelable { 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); Loading @@ -102,7 +179,18 @@ public class SyncStatusInfo implements Parcelable { 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) { Loading @@ -111,12 +199,15 @@ public class SyncStatusInfo implements Parcelable { 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(); Loading Loading @@ -149,25 +240,37 @@ public class SyncStatusInfo implements Parcelable { } 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; Loading Loading @@ -251,4 +354,41 @@ public class SyncStatusInfo implements Parcelable { } } } /** * 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
services/core/java/com/android/server/content/ContentService.java +30 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,8 @@ import android.os.IBinder; 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; Loading Loading @@ -1577,4 +1579,32 @@ public final class ContentService extends IContentService.Stub { } } } 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); } }
services/core/java/com/android/server/content/ContentShellCommand.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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(); } }
services/core/java/com/android/server/content/SyncManager.java +91 −32 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.content.SyncAdaptersCache; 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; Loading Loading @@ -95,6 +96,7 @@ import com.android.internal.notification.SystemNotificationChannels; 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; Loading Loading @@ -122,6 +124,7 @@ import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; /** Loading Loading @@ -242,7 +245,7 @@ public class SyncManager { /** Track whether the device has already been provisioned. */ private volatile boolean mProvisioned; protected SyncAdaptersCache mSyncAdapters; protected final SyncAdaptersCache mSyncAdapters; private final Random mRand; Loading Loading @@ -423,6 +426,17 @@ public class SyncManager { } }; 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) { Loading Loading @@ -631,6 +645,9 @@ public class SyncManager { 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); Loading Loading @@ -946,10 +963,14 @@ public class SyncManager { source = SyncStorageEngine.SOURCE_USER; } else if (requestedAuthority == null) { source = SyncStorageEngine.SOURCE_POLL; } else { 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_SERVER; source = SyncStorageEngine.SOURCE_OTHER; } } for (AccountAndUser account : accounts) { Loading Loading @@ -2134,6 +2155,7 @@ public class SyncManager { 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(); Loading Loading @@ -2181,26 +2203,35 @@ public class SyncManager { 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 = Loading Loading @@ -2234,37 +2265,50 @@ public class SyncManager { } table.set(row, 0, authority, settings.syncable, settings.enabled); QuadConsumer<String, Stats, Function<Integer, String>, Integer> c = (label, stats, filter, r) -> { sb.setLength(0); table.set(row, 4, status.numSourceLocal, status.numSourcePoll, status.numSourcePeriodic, status.numSourceServer, status.numSourceUser, status.numSyncs, formatDurationHMS(sb, status.totalElapsedTime)); 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); Loading @@ -2274,6 +2318,7 @@ public class SyncManager { 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); Loading @@ -2299,6 +2344,10 @@ public class SyncManager { } } 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'); Loading Loading @@ -2459,6 +2508,7 @@ public class SyncManager { 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(); Loading Loading @@ -2525,6 +2575,7 @@ public class SyncManager { } 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; Loading Loading @@ -3104,6 +3155,10 @@ public class SyncManager { 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) { Loading Loading @@ -4015,4 +4070,8 @@ public class SyncManager { Slog.wtf(TAG, message); mLogger.log("WTF: ", message); } public void resetTodayStats() { mSyncStorageEngine.resetTodayStats(/*force=*/ true); } }