Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 38f0cb49 authored by Makoto Onuki's avatar Makoto Onuki Committed by android-build-merger
Browse files

Merge "Add 1-day sync stats in syncmanager dumpsys." into pi-dev

am: d7732b4c

Change-Id: I7eeab220ca2d90623868e1e3f724961ad660da42
parents 98145fa0 d7732b4c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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();
}
+173 −33
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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) {
@@ -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();
@@ -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;
@@ -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
+30 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);
    }
}
+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();
    }
}
+91 −32
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;

/**
@@ -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;

@@ -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) {
@@ -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);
@@ -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) {
@@ -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();

@@ -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 =
@@ -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);
@@ -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);
@@ -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');
@@ -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();
@@ -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;
@@ -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) {
@@ -4015,4 +4070,8 @@ public class SyncManager {
        Slog.wtf(TAG, message);
        mLogger.log("WTF: ", message);
    }

    public void resetTodayStats() {
        mSyncStorageEngine.resetTodayStats(/*force=*/ true);
    }
}
Loading