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

Commit 5f01cb6d authored by Neil Fuller's avatar Neil Fuller Committed by Gerrit Code Review
Browse files

Merge "Add a new method to set time"

parents 18fa446f 3aedd492
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app.timedetector;

import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;

/**
@@ -33,4 +34,5 @@ import android.app.timedetector.PhoneTimeSuggestion;
 */
interface ITimeDetectorService {
  void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
  void suggestManualTime(in ManualTimeSuggestion timeSuggestion);
}
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.app.timedetector;

parcelable ManualTimeSuggestion;
+127 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.app.timedetector;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.TimestampedValue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * A time signal from a manual (user provided) source. The value consists of the number of
 * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
 * clock when that number was established. The elapsed realtime clock is considered accurate but
 * volatile, so time signals must not be persisted across device resets.
 *
 * @hide
 */
public final class ManualTimeSuggestion implements Parcelable {

    public static final @NonNull Creator<ManualTimeSuggestion> CREATOR =
            new Creator<ManualTimeSuggestion>() {
                public ManualTimeSuggestion createFromParcel(Parcel in) {
                    return ManualTimeSuggestion.createFromParcel(in);
                }

                public ManualTimeSuggestion[] newArray(int size) {
                    return new ManualTimeSuggestion[size];
                }
            };

    @NonNull
    private final TimestampedValue<Long> mUtcTime;
    @Nullable
    private ArrayList<String> mDebugInfo;

    public ManualTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
        mUtcTime = Objects.requireNonNull(utcTime);
    }

    private static ManualTimeSuggestion createFromParcel(Parcel in) {
        TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
        ManualTimeSuggestion suggestion = new ManualTimeSuggestion(utcTime);
        @SuppressWarnings("unchecked")
        ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
        suggestion.mDebugInfo = debugInfo;
        return suggestion;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeParcelable(mUtcTime, 0);
        dest.writeList(mDebugInfo);
    }

    @NonNull
    public TimestampedValue<Long> getUtcTime() {
        return mUtcTime;
    }

    @NonNull
    public List<String> getDebugInfo() {
        return Collections.unmodifiableList(mDebugInfo);
    }

    /**
     * Associates information with the instance that can be useful for debugging / logging. The
     * information is present in {@link #toString()} but is not considered for
     * {@link #equals(Object)} and {@link #hashCode()}.
     */
    public void addDebugInfo(String... debugInfos) {
        if (mDebugInfo == null) {
            mDebugInfo = new ArrayList<>();
        }
        mDebugInfo.addAll(Arrays.asList(debugInfos));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        ManualTimeSuggestion that = (ManualTimeSuggestion) o;
        return Objects.equals(mUtcTime, that.mUtcTime);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mUtcTime);
    }

    @Override
    public String toString() {
        return "ManualTimeSuggestion{"
                + "mUtcTime=" + mUtcTime
                + ", mDebugInfo=" + mDebugInfo
                + '}';
    }
}
+33 −4
Original line number Diff line number Diff line
@@ -17,19 +17,22 @@
package android.app.timedetector;

import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemClock;
import android.util.Log;
import android.util.TimestampedValue;

/**
 * The interface through which system components can send signals to the TimeDetectorService.
 * @hide
 */
@SystemService(Context.TIME_DETECTOR_SERVICE)
public final class TimeDetector {
public class TimeDetector {
    private static final String TAG = "timedetector.TimeDetector";
    private static final boolean DEBUG = false;

@@ -41,10 +44,11 @@ public final class TimeDetector {
    }

    /**
     * Suggests the current time to the detector. The detector may ignore the signal if better
     * signals are available such as those that come from more reliable sources or were
     * determined more recently.
     * Suggests the current phone-signal derived time to the detector. The detector may ignore the
     * signal if better signals are available such as those that come from more reliable sources or
     * were determined more recently.
     */
    @RequiresPermission(android.Manifest.permission.SET_TIME)
    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
        if (DEBUG) {
            Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion);
@@ -56,4 +60,29 @@ public final class TimeDetector {
        }
    }

    /**
     * Suggests the user's manually entered current time to the detector.
     */
    @RequiresPermission(android.Manifest.permission.SET_TIME)
    public void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion) {
        if (DEBUG) {
            Log.d(TAG, "suggestManualTime called: " + timeSuggestion);
        }
        try {
            mITimeDetectorService.suggestManualTime(timeSuggestion);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * A shared utility method to create a {@link ManualTimeSuggestion}.
     */
    public static ManualTimeSuggestion createManualTimeSuggestion(long when, String why) {
        TimestampedValue<Long> utcTime =
                new TimestampedValue<>(SystemClock.elapsedRealtime(), when);
        ManualTimeSuggestion manualTimeSuggestion = new ManualTimeSuggestion(utcTime);
        manualTimeSuggestion.addDebugInfo(why);
        return manualTimeSuggestion;
    }
}
+85 −46
Original line number Diff line number Diff line
@@ -16,9 +16,11 @@

package com.android.server.timedetector;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Intent;
import android.util.Slog;
@@ -27,6 +29,8 @@ import android.util.TimestampedValue;
import com.android.internal.telephony.TelephonyIntents;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
@@ -38,10 +42,22 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {

    private final static String TAG = "timedetector.SimpleTimeDetectorStrategy";

    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Origin {}

    /** Used when a time value originated from a telephony signal. */
    @Origin
    private static final int ORIGIN_PHONE = 1;

    /** Used when a time value originated from a user / manual settings. */
    @Origin
    private static final int ORIGIN_MANUAL = 2;

    /**
     * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
     * actual system clock time before a warning is logged. Used to help identify situations where
     * there is something other than this class setting the system clock.
     * there is something other than this class setting the system clock automatically.
     */
    private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;

@@ -52,11 +68,11 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
    @Nullable private PhoneTimeSuggestion mLastPhoneSuggestion;

    // Information about the last time signal received: Used when toggling auto-time.
    @Nullable private TimestampedValue<Long> mLastSystemClockTime;
    private boolean mLastSystemClockTimeSendNetworkBroadcast;
    @Nullable private TimestampedValue<Long> mLastAutoSystemClockTime;
    private boolean mLastAutoSystemClockTimeSendNetworkBroadcast;

    // System clock state.
    @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet;
    @Nullable private TimestampedValue<Long> mLastAutoSystemClockTimeSet;

    @Override
    public void initialize(@NonNull Callback callback) {
@@ -78,17 +94,18 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
            return;
        }
        // Always store the last NITZ value received, regardless of whether we go on to use it to
        // update the system clock. This is so that we can validate future NITZ signals.
        // update the system clock. This is so that we can validate future phone suggestions.
        mLastPhoneSuggestion = timeSuggestion;

        // System clock update logic.
        final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
        setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, timeSuggestion);
    }

        // Historically, Android has sent a telephony broadcast only when setting the time using
        // NITZ.
        final boolean sendNetworkBroadcast = true;

    @Override
    public void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
        final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
        setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
        setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, timeSuggestion);
    }

    private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion,
@@ -110,54 +127,76 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
    }

    private void setSystemClockIfRequired(
            TimestampedValue<Long> time, boolean sendNetworkBroadcast) {

        // Store the last candidate we've seen in all cases so we can set the system clock
        // when/if time detection is enabled.
        mLastSystemClockTime = time;
        mLastSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;

        if (!mCallback.isTimeDetectionEnabled()) {
            Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time);
            @Origin int origin, TimestampedValue<Long> time, Object cause) {
        // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only
        // when setting the time using NITZ.
        boolean sendNetworkBroadcast = origin == ORIGIN_PHONE;

        boolean isOriginAutomatic = isOriginAutomatic(origin);
        if (isOriginAutomatic) {
            // Store the last auto time candidate we've seen in all cases so we can set the system
            // clock when/if time detection is off but later enabled.
            mLastAutoSystemClockTime = time;
            mLastAutoSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;

            if (!mCallback.isAutoTimeDetectionEnabled()) {
                Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is not enabled."
                        + " time=" + time
                        + ", cause=" + cause);
                return;
            }
        } else {
            if (mCallback.isAutoTimeDetectionEnabled()) {
                Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is enabled."
                        + " time=" + time
                        + ", cause=" + cause);
                return;
            }
        }

        mCallback.acquireWakeLock();
        try {
            long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
            long actualTimeMillis = mCallback.systemClockMillis();

            if (isOriginAutomatic) {
                // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
                // may be setting the clock.
            if (mLastSystemClockTimeSet != null) {
                if (mLastAutoSystemClockTimeSet != null) {
                    long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
                        mLastSystemClockTimeSet, elapsedRealtimeMillis);
                            mLastAutoSystemClockTimeSet, elapsedRealtimeMillis);
                    long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis);
                    if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
                    Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may"
                            + " be inaccurate or something unexpectedly set the system clock."
                        Slog.w(TAG,
                                "System clock has not tracked elapsed real time clock. A clock may"
                                        + " be inaccurate or something unexpectedly set the system"
                                        + " clock."
                                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                                        + " expectedTimeMillis=" + expectedTimeMillis
                                        + " actualTimeMillis=" + actualTimeMillis);
                    }
                }
            }

            final String reason = "New time signal";
            adjustAndSetDeviceSystemClock(
                    time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason);
                    time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, cause);
        } finally {
            mCallback.releaseWakeLock();
        }
    }

    private static boolean isOriginAutomatic(@Origin int origin) {
        return origin == ORIGIN_PHONE;
    }

    @Override
    public void handleAutoTimeDetectionToggle(boolean enabled) {
        // If automatic time detection is enabled we update the system clock instantly if we can.
        // Conversely, if automatic time detection is disabled we leave the clock as it is.
        if (enabled) {
            if (mLastSystemClockTime != null) {
            if (mLastAutoSystemClockTime != null) {
                // Only send the network broadcast if the last candidate would have caused one.
                final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast;
                final boolean sendNetworkBroadcast = mLastAutoSystemClockTimeSendNetworkBroadcast;

                mCallback.acquireWakeLock();
                try {
@@ -165,7 +204,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
                    long actualTimeMillis = mCallback.systemClockMillis();

                    final String reason = "Automatic time detection enabled.";
                    adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast,
                    adjustAndSetDeviceSystemClock(mLastAutoSystemClockTime, sendNetworkBroadcast,
                            elapsedRealtimeMillis, actualTimeMillis, reason);
                } finally {
                    mCallback.releaseWakeLock();
@@ -174,22 +213,22 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
        } else {
            // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
            // it should be in future.
            mLastSystemClockTimeSet = null;
            mLastAutoSystemClockTimeSet = null;
        }
    }

    @Override
    public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
        pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
        pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet);
        pw.println("mLastSystemClockTime=" + mLastSystemClockTime);
        pw.println("mLastSystemClockTimeSendNetworkBroadcast="
                + mLastSystemClockTimeSendNetworkBroadcast);
        pw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
        pw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
        pw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
                + mLastAutoSystemClockTimeSendNetworkBroadcast);
    }

    private void adjustAndSetDeviceSystemClock(
            TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
            long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) {
            long elapsedRealtimeMillis, long actualSystemClockMillis, Object cause) {

        // Adjust for the time that has elapsed since the signal was received.
        long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
@@ -203,20 +242,20 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
                    + " system clock are close enough."
                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                    + " newTime=" + newTime
                    + " reason=" + reason
                    + " cause=" + cause
                    + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
                    + " absTimeDifference=" + absTimeDifference);
            return;
        }

        Slog.d(TAG, "Setting system clock using time=" + newTime
                + " reason=" + reason
                + " cause=" + cause
                + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                + " newTimeMillis=" + newSystemClockMillis);
        mCallback.setSystemClock(newSystemClockMillis);

        // CLOCK_PARANOIA : Record the last time this class set the system clock.
        mLastSystemClockTimeSet = newTime;
        mLastAutoSystemClockTimeSet = newTime;

        if (sendNetworkBroadcast) {
            // Send a broadcast that telephony code used to send after setting the clock.
Loading