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

Commit af3eeaf4 authored by Neil Fuller's avatar Neil Fuller
Browse files

Make TimeDetectorService more aware of origin

Make TimeDetectorService more aware of a time signal's origin by making
the time signal from telephony less generic.

When TimeDetectorService was written it was assumed that the TimeSignal
could be generic and there would just be a String source identifier to
indicate origin.

With multiple SIMs / phones on a device it makes sense to keep track of
the originating phone of the signal so that the time detector service
could implement stickiness if it wanted. This would avoid a device
alternating back and forth between two signals and help with debugging.

As new signals are added, new "suggestXxxxTime()" methods will be added
to TimeDetectorService. That is the point at which the strategy for
ranking different sources can be decided. e.g. should NTP always rank
higher than NITZ?

The main change here is that TimeSignal is renamed to
PhoneTimeSuggestion to indicate it's only for use by signals from
telephony, sourceId has been removed and it has been given a phoneId
instead. Extra free-form metadata (mDebugInfo) has been added to assist
with logging / debugging.

Bug:140712361
Test: atest android.app.timedetector
Test: atest com.android.server.timedetector
Change-Id: I5305c34516e5f4e254309193183f00e054801a46
parent 8a9a9ada
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -16,10 +16,10 @@

package android.app.timedetector;

import android.app.timedetector.TimeSignal;
import android.app.timedetector.PhoneTimeSuggestion;

/**
 * System private API to comunicate with time detector service.
 * System private API to communicate with time detector service.
 *
 * <p>Used by parts of the Android system with signals associated with the device's time to provide
 * information to the Time Detector Service.
@@ -32,5 +32,5 @@ import android.app.timedetector.TimeSignal;
 * {@hide}
 */
interface ITimeDetectorService {
  void suggestTime(in TimeSignal timeSignal);
  void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
}
+1 −1
Original line number Diff line number Diff line
@@ -16,4 +16,4 @@

package android.app.timedetector;

parcelable TimeSignal;
 No newline at end of file
parcelable PhoneTimeSuggestion;
+137 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 * 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.
@@ -17,47 +17,57 @@
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 named 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
 * A time signal from a telephony 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 TimeSignal implements Parcelable {
public final class PhoneTimeSuggestion implements Parcelable {

    public static final @android.annotation.NonNull Parcelable.Creator<TimeSignal> CREATOR =
            new Parcelable.Creator<TimeSignal>() {
                public TimeSignal createFromParcel(Parcel in) {
                    return TimeSignal.createFromParcel(in);
    public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR =
            new Parcelable.Creator<PhoneTimeSuggestion>() {
                public PhoneTimeSuggestion createFromParcel(Parcel in) {
                    return PhoneTimeSuggestion.createFromParcel(in);
                }

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

    public static final String SOURCE_ID_NITZ = "nitz";

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

    public TimeSignal(String sourceId, TimestampedValue<Long> utcTime) {
        mSourceId = Objects.requireNonNull(sourceId);
    public PhoneTimeSuggestion(int phoneId, @NonNull TimestampedValue<Long> utcTime) {
        mPhoneId = phoneId;
        mUtcTime = Objects.requireNonNull(utcTime);
    }

    private static TimeSignal createFromParcel(Parcel in) {
        String sourceId = in.readString();
    private static PhoneTimeSuggestion createFromParcel(Parcel in) {
        int phoneId = in.readInt();
        TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
        return new TimeSignal(sourceId, utcTime);
        PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId, utcTime);
        @SuppressWarnings("unchecked")
        ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
        suggestion.mDebugInfo = debugInfo;
        return suggestion;
    }

    @Override
@@ -67,13 +77,13 @@ public final class TimeSignal implements Parcelable {

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

    @NonNull
    public String getSourceId() {
        return mSourceId;
    public int getPhoneId() {
        return mPhoneId;
    }

    @NonNull
@@ -81,6 +91,23 @@ public final class TimeSignal implements Parcelable {
        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) {
@@ -89,21 +116,22 @@ public final class TimeSignal implements Parcelable {
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        TimeSignal that = (TimeSignal) o;
        return Objects.equals(mSourceId, that.mSourceId)
        PhoneTimeSuggestion that = (PhoneTimeSuggestion) o;
        return mPhoneId == that.mPhoneId
                && Objects.equals(mUtcTime, that.mUtcTime);
    }

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

    @Override
    public String toString() {
        return "TimeSignal{"
                + "mSourceId='" + mSourceId + '\''
        return "PhoneTimeSuggestion{"
                + "mPhoneId='" + mPhoneId + '\''
                + ", mUtcTime=" + mUtcTime
                + ", mDebugInfo=" + mDebugInfo
                + '}';
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -45,12 +45,12 @@ public final class TimeDetector {
     * signals are available such as those that come from more reliable sources or were
     * determined more recently.
     */
    public void suggestTime(@NonNull TimeSignal timeSignal) {
    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
        if (DEBUG) {
            Log.d(TAG, "suggestTime called: " + timeSignal);
            Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion);
        }
        try {
            mITimeDetectorService.suggestTime(timeSignal);
            mITimeDetectorService.suggestPhoneTime(timeSuggestion);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+75 −0
Original line number Diff line number Diff line
/*
 * Copyright 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;

import android.os.Parcel;
import android.os.Parcelable;
import android.util.TimestampedValue;

import org.junit.Test;

public class PhoneTimeSuggestionTest {
    private static final int PHONE_ID = 99999;

    @Test
    public void testEquals() {
        PhoneTimeSuggestion one =
                new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
        assertEquals(one, one);

        PhoneTimeSuggestion two =
                new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
        assertEquals(one, two);
        assertEquals(two, one);

        PhoneTimeSuggestion three =
                new PhoneTimeSuggestion(PHONE_ID + 1, new TimestampedValue<>(1111L, 2222L));
        assertNotEquals(one, three);
        assertNotEquals(three, one);

        // DebugInfo must not be considered in equals().
        one.addDebugInfo("Debug info 1");
        two.addDebugInfo("Debug info 2");
        assertEquals(one, two);
    }

    @Test
    public void testParcelable() {
        PhoneTimeSuggestion one =
                new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
        assertEquals(one, roundTripParcelable(one));

        // DebugInfo should also be stored (but is not checked by equals()
        one.addDebugInfo("This is debug info");
        PhoneTimeSuggestion two = roundTripParcelable(one);
        assertEquals(one.getDebugInfo(), two.getDebugInfo());
    }

    @SuppressWarnings("unchecked")
    private static <T extends Parcelable> T roundTripParcelable(T one) {
        Parcel parcel = Parcel.obtain();
        parcel.writeTypedObject(one, 0);
        parcel.setDataPosition(0);

        T toReturn = (T) parcel.readTypedObject(PhoneTimeSuggestion.CREATOR);
        parcel.recycle();
        return toReturn;
    }
}
Loading