Merge "Simple pass-through TimeDetectorService"
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.app.timedetector;
|
||||
|
||||
import android.app.timedetector.TimeSignal;
|
||||
|
||||
/**
|
||||
* System private API to comunicate with time detector service.
|
||||
*
|
||||
@@ -30,5 +32,5 @@ package android.app.timedetector;
|
||||
* {@hide}
|
||||
*/
|
||||
interface ITimeDetectorService {
|
||||
void stubbedCall();
|
||||
void suggestTime(in TimeSignal timeSignal);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.app.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
@@ -40,15 +41,16 @@ public final class TimeDetector {
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
* TODO: Remove this when the service implementation is built out.
|
||||
* 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.
|
||||
*/
|
||||
public void stubbedCall() {
|
||||
public void suggestTime(@NonNull TimeSignal timeSignal) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "stubbedCall called");
|
||||
Log.d(TAG, "suggestTime called: " + timeSignal);
|
||||
}
|
||||
try {
|
||||
mITimeDetectorService.stubbedCall();
|
||||
mITimeDetectorService.suggestTime(timeSignal);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
19
core/java/android/app/timedetector/TimeSignal.aidl
Normal file
19
core/java/android/app/timedetector/TimeSignal.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 TimeSignal;
|
||||
110
core/java/android/app/timedetector/TimeSignal.java
Normal file
110
core/java/android/app/timedetector/TimeSignal.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 android.app.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
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
|
||||
* must not be persisted across device resets.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class TimeSignal implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<TimeSignal> CREATOR =
|
||||
new Parcelable.Creator<TimeSignal>() {
|
||||
public TimeSignal createFromParcel(Parcel in) {
|
||||
return TimeSignal.createFromParcel(in);
|
||||
}
|
||||
|
||||
public TimeSignal[] newArray(int size) {
|
||||
return new TimeSignal[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static final String SOURCE_ID_NITZ = "nitz";
|
||||
|
||||
private final String mSourceId;
|
||||
private final TimestampedValue<Long> mUtcTime;
|
||||
|
||||
public TimeSignal(String sourceId, TimestampedValue<Long> utcTime) {
|
||||
mSourceId = Objects.requireNonNull(sourceId);
|
||||
mUtcTime = Objects.requireNonNull(utcTime);
|
||||
}
|
||||
|
||||
private static TimeSignal createFromParcel(Parcel in) {
|
||||
String sourceId = in.readString();
|
||||
TimestampedValue<Long> utcTime =
|
||||
TimestampedValue.readFromParcel(in, null /* classLoader */, Long.class);
|
||||
return new TimeSignal(sourceId, utcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(mSourceId);
|
||||
TimestampedValue.writeToParcel(dest, mUtcTime);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getSourceId() {
|
||||
return mSourceId;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TimestampedValue<Long> getUtcTime() {
|
||||
return mUtcTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TimeSignal that = (TimeSignal) o;
|
||||
return Objects.equals(mSourceId, that.mSourceId)
|
||||
&& Objects.equals(mUtcTime, that.mUtcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mSourceId, mUtcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TimeSignal{"
|
||||
+ "mSourceId='" + mSourceId + '\''
|
||||
+ ", mUtcTime=" + mUtcTime
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
121
core/java/android/util/TimestampedValue.java
Normal file
121
core/java/android/util/TimestampedValue.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 android.util;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A value with an associated reference time. The reference time will typically be provided by the
|
||||
* elapsed realtime clock. The elapsed realtime clock can be obtained using methods like
|
||||
* {@link SystemClock#elapsedRealtime()} or {@link SystemClock#elapsedRealtimeClock()}.
|
||||
* If a suitable clock is used the reference time can be used to identify the age of a value or
|
||||
* ordering between values.
|
||||
*
|
||||
* <p>To read and write a timestamped value from / to a Parcel see
|
||||
* {@link #readFromParcel(Parcel, ClassLoader, Class)} and
|
||||
* {@link #writeToParcel(Parcel, TimestampedValue)}.
|
||||
*
|
||||
* @param <T> the type of the value with an associated timestamp
|
||||
* @hide
|
||||
*/
|
||||
public final class TimestampedValue<T> {
|
||||
private final long mReferenceTimeMillis;
|
||||
private final T mValue;
|
||||
|
||||
public TimestampedValue(long referenceTimeMillis, T value) {
|
||||
mReferenceTimeMillis = referenceTimeMillis;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public long getReferenceTimeMillis() {
|
||||
return mReferenceTimeMillis;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TimestampedValue<?> that = (TimestampedValue<?>) o;
|
||||
return mReferenceTimeMillis == that.mReferenceTimeMillis
|
||||
&& Objects.equals(mValue, that.mValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mReferenceTimeMillis, mValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a {@link TimestampedValue} from a parcel that was stored using
|
||||
* {@link #writeToParcel(Parcel, TimestampedValue)}.
|
||||
*
|
||||
* <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
|
||||
* and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
|
||||
* supported by those methods.
|
||||
*
|
||||
* @param in the Parcel to read from
|
||||
* @param classLoader the ClassLoader to pass to {@link Parcel#readValue(ClassLoader)}
|
||||
* @param valueClass the expected type of the value, typically the same as {@code <T>} but can
|
||||
* also be a subclass
|
||||
* @throws RuntimeException if the value read is not compatible with {@code valueClass} or the
|
||||
* object could not be read
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@NonNull
|
||||
public static <T> TimestampedValue<T> readFromParcel(
|
||||
@NonNull Parcel in, @Nullable ClassLoader classLoader, Class<? extends T> valueClass) {
|
||||
long referenceTimeMillis = in.readLong();
|
||||
T value = (T) in.readValue(classLoader);
|
||||
// Equivalent to static code: if (!(value.getClass() instanceof {valueClass})) {
|
||||
if (value != null && !valueClass.isAssignableFrom(value.getClass())) {
|
||||
throw new RuntimeException("Value was of type " + value.getClass()
|
||||
+ " is not assignable to " + valueClass);
|
||||
}
|
||||
return new TimestampedValue<>(referenceTimeMillis, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a {@link TimestampedValue} to a parcel so that it can be read using
|
||||
* {@link #readFromParcel(Parcel, ClassLoader, Class)}.
|
||||
*
|
||||
* <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
|
||||
* and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
|
||||
* supported by those methods.
|
||||
*
|
||||
* @param dest the Parcel
|
||||
* @param timestampedValue the value
|
||||
* @throws RuntimeException if the value could not be written to the Parcel
|
||||
*/
|
||||
public static void writeToParcel(
|
||||
@NonNull Parcel dest, @NonNull TimestampedValue<?> timestampedValue) {
|
||||
dest.writeLong(timestampedValue.mReferenceTimeMillis);
|
||||
dest.writeValue(timestampedValue.mValue);
|
||||
}
|
||||
}
|
||||
119
core/tests/coretests/src/android/util/TimestampedValueTest.java
Normal file
119
core/tests/coretests/src/android/util/TimestampedValueTest.java
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 android.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TimestampedValueTest {
|
||||
|
||||
@Test
|
||||
public void testEqualsAndHashcode() {
|
||||
TimestampedValue<String> one1000one = new TimestampedValue<>(1000, "one");
|
||||
assertEqualsAndHashCode(one1000one, one1000one);
|
||||
|
||||
TimestampedValue<String> one1000two = new TimestampedValue<>(1000, "one");
|
||||
assertEqualsAndHashCode(one1000one, one1000two);
|
||||
|
||||
TimestampedValue<String> two1000 = new TimestampedValue<>(1000, "two");
|
||||
assertNotEquals(one1000one, two1000);
|
||||
|
||||
TimestampedValue<String> one2000 = new TimestampedValue<>(2000, "one");
|
||||
assertNotEquals(one1000one, one2000);
|
||||
}
|
||||
|
||||
private static void assertEqualsAndHashCode(Object one, Object two) {
|
||||
assertEquals(one, two);
|
||||
assertEquals(one.hashCode(), two.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParceling() {
|
||||
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
|
||||
Parcel parcel = Parcel.obtain();
|
||||
try {
|
||||
TimestampedValue.writeToParcel(parcel, stringValue);
|
||||
|
||||
parcel.setDataPosition(0);
|
||||
|
||||
TimestampedValue<String> stringValueCopy =
|
||||
TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
|
||||
assertEquals(stringValue, stringValueCopy);
|
||||
} finally {
|
||||
parcel.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParceling_valueClassOk() {
|
||||
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
|
||||
Parcel parcel = Parcel.obtain();
|
||||
try {
|
||||
TimestampedValue.writeToParcel(parcel, stringValue);
|
||||
|
||||
parcel.setDataPosition(0);
|
||||
|
||||
TimestampedValue<Object> stringValueCopy =
|
||||
TimestampedValue.readFromParcel(parcel, null /* classLoader */, Object.class);
|
||||
assertEquals(stringValue, stringValueCopy);
|
||||
} finally {
|
||||
parcel.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParceling_valueClassIncompatible() {
|
||||
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
|
||||
Parcel parcel = Parcel.obtain();
|
||||
try {
|
||||
TimestampedValue.writeToParcel(parcel, stringValue);
|
||||
|
||||
parcel.setDataPosition(0);
|
||||
|
||||
TimestampedValue.readFromParcel(parcel, null /* classLoader */, Double.class);
|
||||
fail();
|
||||
} catch (RuntimeException expected) {
|
||||
} finally {
|
||||
parcel.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParceling_nullValue() {
|
||||
TimestampedValue<String> nullValue = new TimestampedValue<>(1000, null);
|
||||
Parcel parcel = Parcel.obtain();
|
||||
try {
|
||||
TimestampedValue.writeToParcel(parcel, nullValue);
|
||||
|
||||
parcel.setDataPosition(0);
|
||||
|
||||
TimestampedValue<Object> nullValueCopy =
|
||||
TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
|
||||
assertEquals(nullValue, nullValueCopy);
|
||||
} finally {
|
||||
parcel.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* A placeholder implementation of TimeDetectorStrategy that passes NITZ suggestions immediately
|
||||
* to {@link AlarmManager}.
|
||||
*/
|
||||
public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
|
||||
private final static String TAG = "timedetector.SimpleTimeDetectorStrategy";
|
||||
|
||||
private Callback mHelper;
|
||||
|
||||
@Override
|
||||
public void initialize(@NonNull Callback callback) {
|
||||
mHelper = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestTime(@NonNull TimeSignal timeSignal) {
|
||||
if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) {
|
||||
Slog.w(TAG, "Ignoring signal from unknown source: " + timeSignal);
|
||||
return;
|
||||
}
|
||||
|
||||
mHelper.setTime(timeSignal.getUtcTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) {
|
||||
// No state to dump.
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,19 @@
|
||||
|
||||
package com.android.server.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.timedetector.ITimeDetectorService;
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import android.app.timedetector.ITimeDetectorService;
|
||||
import android.content.Context;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class TimeDetectorService extends ITimeDetectorService.Stub {
|
||||
|
||||
@@ -47,26 +51,36 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final TimeDetectorStrategy mTimeDetectorStrategy;
|
||||
|
||||
private static TimeDetectorService create(Context context) {
|
||||
return new TimeDetectorService(context);
|
||||
TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
|
||||
timeDetector.initialize(new TimeDetectorStrategyCallbackImpl(context));
|
||||
return new TimeDetectorService(context, timeDetector);
|
||||
}
|
||||
|
||||
public TimeDetectorService(Context context) {
|
||||
mContext = context;
|
||||
@VisibleForTesting
|
||||
public TimeDetectorService(@NonNull Context context,
|
||||
@NonNull TimeDetectorStrategy timeDetectorStrategy) {
|
||||
mContext = Objects.requireNonNull(context);
|
||||
mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stubbedCall() {
|
||||
// Empty call for initial tests.
|
||||
Slog.d(TAG, "stubbedCall() called");
|
||||
// TODO(nfuller): Remove when there are real methods.
|
||||
public void suggestTime(@NonNull TimeSignal timeSignal) {
|
||||
enforceSetTimePermission();
|
||||
mTimeDetectorStrategy.suggestTime(timeSignal);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
|
||||
@Nullable String[] args) {
|
||||
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
|
||||
|
||||
// TODO(nfuller): Implement when there is state.
|
||||
mTimeDetectorStrategy.dump(fd, pw, args);
|
||||
}
|
||||
|
||||
private void enforceSetTimePermission() {
|
||||
mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* The interface for classes that implement the time detection algorithm used by the
|
||||
* TimeDetectorService.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface TimeDetectorStrategy {
|
||||
|
||||
interface Callback {
|
||||
void setTime(TimestampedValue<Long> time);
|
||||
}
|
||||
|
||||
void initialize(@NonNull Callback callback);
|
||||
void suggestTime(@NonNull TimeSignal timeSignal);
|
||||
void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args);
|
||||
|
||||
// Utility methods below are to be moved to a better home when one becomes more obvious.
|
||||
|
||||
/**
|
||||
* Adjusts the supplied time value by applying the difference between the reference time
|
||||
* supplied and the reference time associated with the time.
|
||||
*/
|
||||
static long getTimeAt(@NonNull TimestampedValue<Long> timeValue, long referenceClockMillisNow) {
|
||||
return (referenceClockMillisNow - timeValue.getReferenceTimeMillis())
|
||||
+ timeValue.getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AlarmManager;
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Slog;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
/**
|
||||
* The real implementation of {@link TimeDetectorStrategy.Callback} used on device.
|
||||
*/
|
||||
public class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategy.Callback {
|
||||
|
||||
private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl";
|
||||
|
||||
@NonNull private PowerManager.WakeLock mWakeLock;
|
||||
@NonNull private AlarmManager mAlarmManager;
|
||||
|
||||
public TimeDetectorStrategyCallbackImpl(Context context) {
|
||||
PowerManager powerManager = context.getSystemService(PowerManager.class);
|
||||
|
||||
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||
|
||||
mAlarmManager = context.getSystemService(AlarmManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTime(TimestampedValue<Long> time) {
|
||||
mWakeLock.acquire();
|
||||
try {
|
||||
long elapsedRealtimeMillis = SystemClock.elapsedRealtime();
|
||||
long currentTimeMillis = TimeDetectorStrategy.getTimeAt(time, elapsedRealtimeMillis);
|
||||
Slog.d(TAG, "Setting system clock using time=" + time
|
||||
+ ", elapsedRealtimeMillis=" + elapsedRealtimeMillis);
|
||||
mAlarmManager.setTime(currentTimeMillis);
|
||||
} finally {
|
||||
mWakeLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.timedetector;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SimpleTimeZoneDetectorStrategyTest {
|
||||
|
||||
private TimeDetectorStrategy.Callback mMockCallback;
|
||||
|
||||
private SimpleTimeDetectorStrategy mSimpleTimeZoneDetectorStrategy;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mMockCallback = mock(TimeDetectorStrategy.Callback.class);
|
||||
mSimpleTimeZoneDetectorStrategy = new SimpleTimeDetectorStrategy();
|
||||
mSimpleTimeZoneDetectorStrategy.initialize(mMockCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime_nitz() {
|
||||
TimestampedValue<Long> utcTime = createUtcTime();
|
||||
TimeSignal timeSignal = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime);
|
||||
|
||||
mSimpleTimeZoneDetectorStrategy.suggestTime(timeSignal);
|
||||
|
||||
verify(mMockCallback).setTime(utcTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime_unknownSource() {
|
||||
TimestampedValue<Long> utcTime = createUtcTime();
|
||||
TimeSignal timeSignal = new TimeSignal("unknown", utcTime);
|
||||
mSimpleTimeZoneDetectorStrategy.suggestTime(timeSignal);
|
||||
|
||||
verify(mMockCallback, never()).setTime(any());
|
||||
}
|
||||
|
||||
private static TimestampedValue<Long> createUtcTime() {
|
||||
return new TimestampedValue<>(321L, 123456L);
|
||||
}
|
||||
}
|
||||
@@ -16,31 +16,73 @@
|
||||
|
||||
package com.android.server.timedetector;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.content.Context;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link TimeDetectorService}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TimeDetectorServiceTest {
|
||||
|
||||
private TimeDetectorService mTimeDetectorService;
|
||||
|
||||
private Context mMockContext;
|
||||
private TimeDetectorStrategy mMockTimeDetectorStrategy;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final Context context = InstrumentationRegistry.getContext();
|
||||
mTimeDetectorService = new TimeDetectorService(context);
|
||||
mMockContext = mock(Context.class);
|
||||
mMockTimeDetectorStrategy = mock(TimeDetectorStrategy.class);
|
||||
mTimeDetectorService = new TimeDetectorService(mMockContext, mMockTimeDetectorStrategy);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
verifyNoMoreInteractions(mMockContext, mMockTimeDetectorStrategy);
|
||||
}
|
||||
|
||||
@Test(expected=SecurityException.class)
|
||||
public void testStubbedCall_withoutPermission() {
|
||||
doThrow(new SecurityException("Mock"))
|
||||
.when(mMockContext).enforceCallingPermission(anyString(), any());
|
||||
TimeSignal timeSignal = createNitzTimeSignal();
|
||||
|
||||
try {
|
||||
mTimeDetectorService.suggestTime(timeSignal);
|
||||
} finally {
|
||||
verify(mMockContext).enforceCallingPermission(
|
||||
eq(android.Manifest.permission.SET_TIME), anyString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStubbedCall() {
|
||||
mTimeDetectorService.stubbedCall();
|
||||
public void testSuggestTime() {
|
||||
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
|
||||
|
||||
TimeSignal timeSignal = createNitzTimeSignal();
|
||||
mTimeDetectorService.suggestTime(timeSignal);
|
||||
|
||||
verify(mMockContext)
|
||||
.enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
|
||||
verify(mMockTimeDetectorStrategy).suggestTime(timeSignal);
|
||||
}
|
||||
|
||||
private static TimeSignal createNitzTimeSignal() {
|
||||
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
|
||||
return new TimeSignal(TimeSignal.SOURCE_ID_NITZ, timeValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.timedetector;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TimeDetectorStrategyTest {
|
||||
|
||||
@Test
|
||||
public void testGetTimeAt() {
|
||||
long timeMillis = 1000L;
|
||||
int referenceTimeMillis = 100;
|
||||
TimestampedValue<Long> timestampedValue =
|
||||
new TimestampedValue<>(referenceTimeMillis, timeMillis);
|
||||
// Reference time is after the timestamp.
|
||||
assertEquals(
|
||||
timeMillis + (125 - referenceTimeMillis),
|
||||
TimeDetectorStrategy.getTimeAt(timestampedValue, 125));
|
||||
|
||||
// Reference time is before the timestamp.
|
||||
assertEquals(
|
||||
timeMillis + (75 - referenceTimeMillis),
|
||||
TimeDetectorStrategy.getTimeAt(timestampedValue, 75));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user