Merge "Allow Cell Location for SYSTEM_UID and ROOT_UID"
diff --git a/Android.bp b/Android.bp
index 773c9b7..f11b6fe 100644
--- a/Android.bp
+++ b/Android.bp
@@ -100,6 +100,7 @@
"core/java/android/app/timedetector/ITimeDetectorService.aidl",
"core/java/android/app/timezone/ICallback.aidl",
"core/java/android/app/timezone/IRulesManager.aidl",
+ "core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl",
"core/java/android/app/usage/ICacheQuotaService.aidl",
"core/java/android/app/usage/IStorageStatsManager.aidl",
"core/java/android/app/usage/IUsageStatsManager.aidl",
diff --git a/api/current.txt b/api/current.txt
index 241117b..da5addc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -68724,7 +68724,7 @@
public final class Pattern implements java.io.Serializable {
method public java.util.function.Predicate<java.lang.String> asPredicate();
method public static java.util.regex.Pattern compile(java.lang.String);
- method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
+ method public static java.util.regex.Pattern compile(java.lang.String, int);
method public int flags();
method public java.util.regex.Matcher matcher(java.lang.CharSequence);
method public static boolean matches(java.lang.String, java.lang.CharSequence);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index aa1738d..26ea068 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -24,6 +24,7 @@
import android.app.job.JobScheduler;
import android.app.timedetector.TimeDetector;
import android.app.timezone.RulesManager;
+import android.app.timezonedetector.TimeZoneDetector;
import android.app.trust.TrustManager;
import android.app.usage.IStorageStatsManager;
import android.app.usage.IUsageStatsManager;
@@ -914,6 +915,13 @@
throws ServiceNotFoundException {
return new TimeDetector();
}});
+ registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class,
+ new CachedServiceFetcher<TimeZoneDetector>() {
+ @Override
+ public TimeZoneDetector createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new TimeZoneDetector();
+ }});
}
/**
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
new file mode 100644
index 0000000..ef2cbab
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.timezonedetector;
+
+/**
+ * System private API to comunicate with time zone detector service.
+ *
+ * <p>Used by parts of the Android system with signals associated with the device's time zone to
+ * provide information to the Time Zone Detector Service.
+ *
+ * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
+ * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
+ * for more complete documentation.
+ *
+ *
+ * {@hide}
+ */
+interface ITimeZoneDetectorService {
+ void stubbedCall();
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
new file mode 100644
index 0000000..be3c764
--- /dev/null
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -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 android.app.timezonedetector;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
+
+/**
+ * The interface through which system components can send signals to the TimeZoneDetectorService.
+ * @hide
+ */
+@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
+public final class TimeZoneDetector {
+
+ private static final String TAG = "timezonedetector.TimeZoneDetector";
+ private static final boolean DEBUG = false;
+
+ private final ITimeZoneDetectorService mITimeZoneDetectorService;
+
+ public TimeZoneDetector() throws ServiceNotFoundException {
+ mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
+
+ }
+ /**
+ * Does nothing.
+ * TODO: Remove this when the service implementation is built out.
+ */
+ public void stubbedCall() {
+ if (DEBUG) {
+ Log.d(TAG, "stubbedCall called");
+ }
+ try {
+ mITimeZoneDetectorService.stubbedCall();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index da3e7cb..9c47672 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3021,6 +3021,7 @@
//@hide: INCIDENT_SERVICE,
COMPANION_DEVICE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
+ //@hide: TIME_ZONE_DETECTOR_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -4107,6 +4108,15 @@
public static final String TIME_DETECTOR_SERVICE = "time_detector";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.app.timezonedetector.ITimeZoneDetectorService}.
+ * @hide
+ *
+ * @see #getSystemService(String)
+ */
+ public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 3e64af4..aca2bb8 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -333,8 +333,16 @@
}
/**
- * String.split() returns [''] when the string to be split is empty. This returns []. This does
- * not remove any empty strings from the result. For example split("a,", "," ) returns {"a", ""}.
+ *
+ * This method yields the same result as {@code text.split(expression, -1)} except that if
+ * {@code text.isEmpty()} then this method returns an empty array whereas
+ * {@code "".split(expression, -1)} would have returned an array with a single {@code ""}.
+ *
+ * The {@code -1} means that trailing empty Strings are not removed from the result; for
+ * example split("a,", "," ) returns {"a", ""}. Note that whether a leading zero-width match
+ * can result in a leading {@code ""} depends on whether your app
+ * {@link android.content.pm.ApplicationInfo#targetSdkVersion targets an SDK version}
+ * {@code <= 28}; see {@link Pattern#split(CharSequence, int)}.
*
* @param text the string to split
* @param expression the regular expression to match
@@ -351,8 +359,16 @@
}
/**
- * Splits a string on a pattern. String.split() returns [''] when the string to be
- * split is empty. This returns []. This does not remove any empty strings from the result.
+ * Splits a string on a pattern. This method yields the same result as
+ * {@code pattern.split(text, -1)} except that if {@code text.isEmpty()} then this method
+ * returns an empty array whereas {@code pattern.split("", -1)} would have returned an array
+ * with a single {@code ""}.
+ *
+ * The {@code -1} means that trailing empty Strings are not removed from the result;
+ * Note that whether a leading zero-width match can result in a leading {@code ""} depends
+ * on whether your app {@link android.content.pm.ApplicationInfo#targetSdkVersion targets
+ * an SDK version} {@code <= 28}; see {@link Pattern#split(CharSequence, int)}.
+ *
* @param text the string to split
* @param pattern the regular expression to match
* @return an array of strings. The array will be empty if text is empty
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/util/TimestampedValue.java
index 75fa18d..1289e4d 100644
--- a/core/java/android/util/TimestampedValue.java
+++ b/core/java/android/util/TimestampedValue.java
@@ -126,4 +126,12 @@
dest.writeLong(timestampedValue.mReferenceTimeMillis);
dest.writeValue(timestampedValue.mValue);
}
+
+ /**
+ * Returns the difference in milliseconds between two instance's reference times.
+ */
+ public static long referenceTimeDifference(
+ @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
+ return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
+ }
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 8d6a280..8cb2dcd 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -535,17 +535,207 @@
return true;
}
+// Utility routine to specialize a zygote child process.
+static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
+ jint runtime_flags, jobjectArray javaRlimits,
+ jlong permittedCapabilities, jlong effectiveCapabilities,
+ jint mount_external, jstring java_se_info, jstring java_se_name,
+ bool is_system_server, bool is_child_zygote, jstring instructionSet,
+ jstring dataDir) {
+ std::string error_msg;
+
+ auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
+ __attribute__ ((noreturn)) {
+ const char* se_name_c_str = nullptr;
+ std::unique_ptr<ScopedUtfChars> se_name;
+ if (java_se_name != nullptr) {
+ se_name.reset(new ScopedUtfChars(env, java_se_name));
+ se_name_c_str = se_name->c_str();
+ }
+ if (se_name_c_str == nullptr && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ const std::string& error_msg = (se_name_c_str == nullptr)
+ ? msg
+ : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
+ env->FatalError(error_msg.c_str());
+ __builtin_unreachable();
+ };
+
+ // Keep capabilities across UID change, unless we're staying root.
+ if (uid != 0) {
+ if (!EnableKeepCapabilities(&error_msg)) {
+ fail_fn(error_msg);
+ }
+ }
+
+ if (!SetInheritable(permittedCapabilities, &error_msg)) {
+ fail_fn(error_msg);
+ }
+ if (!DropCapabilitiesBoundingSet(&error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ bool use_native_bridge = !is_system_server && (instructionSet != NULL)
+ && android::NativeBridgeAvailable();
+ if (use_native_bridge) {
+ ScopedUtfChars isa_string(env, instructionSet);
+ use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
+ }
+ if (use_native_bridge && dataDir == NULL) {
+ // dataDir should never be null if we need to use a native bridge.
+ // In general, dataDir will never be null for normal applications. It can only happen in
+ // special cases (for isolated processes which are not associated with any app). These are
+ // launched by the framework and should not be emulated anyway.
+ use_native_bridge = false;
+ ALOGW("Native bridge will not be used because dataDir == NULL.");
+ }
+
+ if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
+ ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
+ if (errno == ENOTCONN || errno == EROFS) {
+ // When device is actively encrypting, we get ENOTCONN here
+ // since FUSE was mounted before the framework restarted.
+ // When encrypted device is booting, we get EROFS since
+ // FUSE hasn't been created yet by init.
+ // In either case, continue without external storage.
+ } else {
+ fail_fn(error_msg);
+ }
+ }
+
+ if (!is_system_server) {
+ int rc = createProcessGroup(uid, getpid());
+ if (rc != 0) {
+ if (rc == -EROFS) {
+ ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+ } else {
+ ALOGE("createProcessGroup(%d, %d) failed: %s", uid, 0/*pid*/, strerror(-rc));
+ }
+ }
+ }
+
+ if (!SetGids(env, javaGids, &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (!SetRLimits(env, javaRlimits, &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (use_native_bridge) {
+ ScopedUtfChars isa_string(env, instructionSet);
+ ScopedUtfChars data_dir(env, dataDir);
+ android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
+ }
+
+ int rc = setresgid(gid, gid, gid);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
+ }
+
+ // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+ // uid from 0, which clears capabilities. The other alternative is to call
+ // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+ // b/71859146). As the result, privileged syscalls used below still need to be accessible in
+ // app process.
+ SetUpSeccompFilter(uid);
+
+ rc = setresuid(uid, uid, uid);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
+ }
+
+ // The "dumpable" flag of a process, which controls core dump generation, is
+ // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
+ // user or group ID changes. See proc(5) for possible values. In most cases,
+ // the value is 0, so core dumps are disabled for zygote children. However,
+ // when running in a Chrome OS container, the value is already set to 2,
+ // which allows the external crash reporter to collect all core dumps. Since
+ // only system crashes are interested, core dump is disabled for app
+ // processes. This also ensures compliance with CTS.
+ int dumpable = prctl(PR_GET_DUMPABLE);
+ if (dumpable == -1) {
+ ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
+ }
+ if (dumpable == 2 && uid >= AID_APP) {
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
+ ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+ }
+ }
+
+ if (NeedsNoRandomizeWorkaround()) {
+ // Work around ARM kernel ASLR lossage (http://b/5817320).
+ int old_personality = personality(0xffffffff);
+ int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+ if (new_personality == -1) {
+ ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+ }
+ }
+
+ if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
+ &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (!SetSchedulerPolicy(&error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ const char* se_info_c_str = NULL;
+ ScopedUtfChars* se_info = NULL;
+ if (java_se_info != NULL) {
+ se_info = new ScopedUtfChars(env, java_se_info);
+ se_info_c_str = se_info->c_str();
+ if (se_info_c_str == NULL) {
+ fail_fn("se_info_c_str == NULL");
+ }
+ }
+ const char* se_name_c_str = NULL;
+ ScopedUtfChars* se_name = NULL;
+ if (java_se_name != NULL) {
+ se_name = new ScopedUtfChars(env, java_se_name);
+ se_name_c_str = se_name->c_str();
+ if (se_name_c_str == NULL) {
+ fail_fn("se_name_c_str == NULL");
+ }
+ }
+ rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+ is_system_server, se_info_c_str, se_name_c_str));
+ }
+
+ // Make it easier to debug audit logs by setting the main thread's name to the
+ // nice name rather than "app_process".
+ if (se_name_c_str == NULL && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ if (se_name_c_str != NULL) {
+ SetThreadName(se_name_c_str);
+ }
+
+ delete se_info;
+ delete se_name;
+
+ // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+ UnsetChldSignalHandler();
+
+ env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
+ is_system_server, is_child_zygote, instructionSet);
+ if (env->ExceptionCheck()) {
+ fail_fn("Error calling post fork hooks.");
+ }
+}
+
// Utility routine to fork zygote and specialize the child process.
-static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
- jint runtime_flags, jobjectArray javaRlimits,
- jlong permittedCapabilities, jlong effectiveCapabilities,
- jint mount_external,
- jstring java_se_info, jstring java_se_name,
- bool is_system_server, jintArray fdsToClose,
- jintArray fdsToIgnore, bool is_child_zygote,
- jstring instructionSet, jstring dataDir) {
+static pid_t ForkCommon(JNIEnv* env, jstring java_se_name, bool is_system_server,
+ jintArray fdsToClose, jintArray fdsToIgnore) {
SetSignalHandlers();
+ // Block SIGCHLD prior to fork.
sigset_t sigchld;
sigemptyset(&sigchld);
sigaddset(&sigchld, SIGCHLD);
@@ -602,6 +792,7 @@
pid_t pid = fork();
if (pid == 0) {
+ // The child process.
PreApplicationInit();
// Clean up any descriptors which must be closed immediately
@@ -614,186 +805,13 @@
if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
fail_fn(error_msg);
}
-
- if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
- fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
- }
-
- // Keep capabilities across UID change, unless we're staying root.
- if (uid != 0) {
- if (!EnableKeepCapabilities(&error_msg)) {
- fail_fn(error_msg);
- }
- }
-
- if (!SetInheritable(permittedCapabilities, &error_msg)) {
- fail_fn(error_msg);
- }
- if (!DropCapabilitiesBoundingSet(&error_msg)) {
- fail_fn(error_msg);
- }
-
- bool use_native_bridge = !is_system_server && (instructionSet != NULL)
- && android::NativeBridgeAvailable();
- if (use_native_bridge) {
- ScopedUtfChars isa_string(env, instructionSet);
- use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
- }
- if (use_native_bridge && dataDir == NULL) {
- // dataDir should never be null if we need to use a native bridge.
- // In general, dataDir will never be null for normal applications. It can only happen in
- // special cases (for isolated processes which are not associated with any app). These are
- // launched by the framework and should not be emulated anyway.
- use_native_bridge = false;
- ALOGW("Native bridge will not be used because dataDir == NULL.");
- }
-
- if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
- ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
- if (errno == ENOTCONN || errno == EROFS) {
- // When device is actively encrypting, we get ENOTCONN here
- // since FUSE was mounted before the framework restarted.
- // When encrypted device is booting, we get EROFS since
- // FUSE hasn't been created yet by init.
- // In either case, continue without external storage.
- } else {
- fail_fn(error_msg);
- }
- }
-
- if (!is_system_server) {
- int rc = createProcessGroup(uid, getpid());
- if (rc != 0) {
- if (rc == -EROFS) {
- ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
- } else {
- ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
- }
- }
- }
-
- std::string error_msg;
- if (!SetGids(env, javaGids, &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (!SetRLimits(env, javaRlimits, &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (use_native_bridge) {
- ScopedUtfChars isa_string(env, instructionSet);
- ScopedUtfChars data_dir(env, dataDir);
- android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
- }
-
- int rc = setresgid(gid, gid, gid);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
- }
-
- // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
- // uid from 0, which clears capabilities. The other alternative is to call
- // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
- // b/71859146). As the result, privileged syscalls used below still need to be accessible in
- // app process.
- SetUpSeccompFilter(uid);
-
- rc = setresuid(uid, uid, uid);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
- }
-
- // The "dumpable" flag of a process, which controls core dump generation, is
- // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
- // user or group ID changes. See proc(5) for possible values. In most cases,
- // the value is 0, so core dumps are disabled for zygote children. However,
- // when running in a Chrome OS container, the value is already set to 2,
- // which allows the external crash reporter to collect all core dumps. Since
- // only system crashes are interested, core dump is disabled for app
- // processes. This also ensures compliance with CTS.
- int dumpable = prctl(PR_GET_DUMPABLE);
- if (dumpable == -1) {
- ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
- }
- if (dumpable == 2 && uid >= AID_APP) {
- if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
- ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
- }
- }
-
- if (NeedsNoRandomizeWorkaround()) {
- // Work around ARM kernel ASLR lossage (http://b/5817320).
- int old_personality = personality(0xffffffff);
- int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
- if (new_personality == -1) {
- ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
- }
- }
-
- if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
- &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (!SetSchedulerPolicy(&error_msg)) {
- fail_fn(error_msg);
- }
-
- const char* se_info_c_str = NULL;
- ScopedUtfChars* se_info = NULL;
- if (java_se_info != NULL) {
- se_info = new ScopedUtfChars(env, java_se_info);
- se_info_c_str = se_info->c_str();
- if (se_info_c_str == NULL) {
- fail_fn("se_info_c_str == NULL");
- }
- }
- const char* se_name_c_str = NULL;
- ScopedUtfChars* se_name = NULL;
- if (java_se_name != NULL) {
- se_name = new ScopedUtfChars(env, java_se_name);
- se_name_c_str = se_name->c_str();
- if (se_name_c_str == NULL) {
- fail_fn("se_name_c_str == NULL");
- }
- }
- rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
- is_system_server, se_info_c_str, se_name_c_str));
- }
-
- // Make it easier to debug audit logs by setting the main thread's name to the
- // nice name rather than "app_process".
- if (se_name_c_str == NULL && is_system_server) {
- se_name_c_str = "system_server";
- }
- if (se_name_c_str != NULL) {
- SetThreadName(se_name_c_str);
- }
-
- delete se_info;
- delete se_name;
-
- // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
- UnsetChldSignalHandler();
-
- env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
- is_system_server, is_child_zygote, instructionSet);
- if (env->ExceptionCheck()) {
- fail_fn("Error calling post fork hooks.");
- }
- } else if (pid > 0) {
- // the parent process
-
- // We blocked SIGCHLD prior to a fork, we unblock it here.
- if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
- fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
- }
}
+
+ // We blocked SIGCHLD prior to a fork, we unblock it here.
+ if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
+ fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
+ }
+
return pid;
}
@@ -879,22 +897,27 @@
// available.
capabilities &= GetEffectiveCapabilityMask(env);
- return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
- rlimits, capabilities, capabilities, mount_external, se_info,
- se_name, false, fdsToClose, fdsToIgnore, is_child_zygote == JNI_TRUE,
- instructionSet, appDataDir);
+ pid_t pid = ForkCommon(env, se_name, false, fdsToClose, fdsToIgnore);
+ if (pid == 0) {
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+ capabilities, capabilities,
+ mount_external, se_info, se_name, false,
+ is_child_zygote == JNI_TRUE, instructionSet, appDataDir);
+ }
+ return pid;
}
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
jlong effectiveCapabilities) {
- pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
- runtime_flags, rlimits,
- permittedCapabilities, effectiveCapabilities,
- MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
- NULL, false, NULL, NULL);
- if (pid > 0) {
+ pid_t pid = ForkCommon(env, NULL, true, NULL, NULL);
+ if (pid == 0) {
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+ permittedCapabilities, effectiveCapabilities,
+ MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
+ false, NULL, NULL);
+ } else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
gSystemServerPid = pid;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 544c953..12c734b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -501,7 +501,7 @@
When true, overrides the config_tether_upstream_types setting above.
-->
- <bool translatable="false" name="config_tether_upstream_automatic">false</bool>
+ <bool translatable="false" name="config_tether_upstream_automatic">true</bool>
<!-- If the DUN connection for this CDMA device supports more than just DUN -->
<!-- traffic you should list them here. -->
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
index 7117c1b..03b4abd 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -116,4 +116,14 @@
parcel.recycle();
}
}
+
+ @Test
+ public void testReferenceTimeDifference() {
+ TimestampedValue<Long> value1 = new TimestampedValue<>(1000, 123L);
+ assertEquals(0, TimestampedValue.referenceTimeDifference(value1, value1));
+
+ TimestampedValue<Long> value2 = new TimestampedValue<>(1, 321L);
+ assertEquals(999, TimestampedValue.referenceTimeDifference(value1, value2));
+ assertEquals(-999, TimestampedValue.referenceTimeDifference(value2, value1));
+ }
}
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 72a428f..bd34eb8 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -236,10 +236,8 @@
break;
}
- FILE *file = fdopen(fd, "a");
- fprintf(file, "\n%s\n", cachesOutput.string());
- fprintf(file, "\nPipeline=%s\n", pipeline.string());
- fflush(file);
+ dprintf(fd, "\n%s\n", cachesOutput.string());
+ dprintf(fd, "\nPipeline=%s\n", pipeline.string());
}
Readback& RenderThread::readback() {
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index 2c24798..1ff455ea 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -16,318 +16,15 @@
package com.android.server;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.PowerManager;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.NtpTrustedTime;
-import android.util.TimeUtils;
-import android.util.TrustedTime;
-
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.DumpUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import android.os.IBinder;
/**
- * Monitors the network time and updates the system time if it is out of sync
- * and there hasn't been any NITZ update from the carrier recently.
- * If looking up the network time fails for some reason, it tries a few times with a short
- * interval and then resets to checking on longer intervals.
- * <p>
- * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
- * available.
- * </p>
+ * An interface for NetworkTimeUpdateService implementations. Eventually part or all of this service
+ * will be subsumed into {@link com.android.server.timedetector.TimeDetectorService}. In the
+ * meantime this interface allows Android to use either the old or new implementation.
*/
-public class NetworkTimeUpdateService extends Binder {
-
- private static final String TAG = "NetworkTimeUpdateService";
- private static final boolean DBG = false;
-
- private static final int EVENT_AUTO_TIME_CHANGED = 1;
- private static final int EVENT_POLL_NETWORK_TIME = 2;
- private static final int EVENT_NETWORK_CHANGED = 3;
-
- private static final String ACTION_POLL =
- "com.android.server.NetworkTimeUpdateService.action.POLL";
-
- private static final int POLL_REQUEST = 0;
-
- private static final long NOT_SET = -1;
- private long mNitzTimeSetTime = NOT_SET;
- private Network mDefaultNetwork = null;
-
- private Context mContext;
- private TrustedTime mTime;
-
- // NTP lookup is done on this thread and handler
- private Handler mHandler;
- private AlarmManager mAlarmManager;
- private PendingIntent mPendingPollIntent;
- private SettingsObserver mSettingsObserver;
- private ConnectivityManager mCM;
- private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
- // The last time that we successfully fetched the NTP time.
- private long mLastNtpFetchTime = NOT_SET;
- private final PowerManager.WakeLock mWakeLock;
-
- // Normal polling frequency
- private final long mPollingIntervalMs;
- // Try-again polling interval, in case the network request failed
- private final long mPollingIntervalShorterMs;
- // Number of times to try again
- private final int mTryAgainTimesMax;
- // If the time difference is greater than this threshold, then update the time.
- private final int mTimeErrorThresholdMs;
- // Keeps track of how many quick attempts were made to fetch NTP time.
- // During bootup, the network may not have been up yet, or it's taking time for the
- // connection to happen.
- private int mTryAgainCounter;
-
- public NetworkTimeUpdateService(Context context) {
- mContext = context;
- mTime = NtpTrustedTime.getInstance(context);
- mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- Intent pollIntent = new Intent(ACTION_POLL, null);
- mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
-
- mPollingIntervalMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpPollingInterval);
- mPollingIntervalShorterMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpPollingIntervalShorter);
- mTryAgainTimesMax = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpRetry);
- mTimeErrorThresholdMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpThreshold);
-
- mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, TAG);
- }
+public interface NetworkTimeUpdateService extends IBinder {
/** Initialize the receivers and initiate the first NTP request */
- public void systemRunning() {
- registerForTelephonyIntents();
- registerForAlarms();
-
- HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- mHandler = new MyHandler(thread.getLooper());
- mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
- mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
-
- mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
- mSettingsObserver.observe(mContext);
- }
-
- private void registerForTelephonyIntents() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
- mContext.registerReceiver(mNitzReceiver, intentFilter);
- }
-
- private void registerForAlarms() {
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
- }
- }, new IntentFilter(ACTION_POLL));
- }
-
- private void onPollNetworkTime(int event) {
- // If Automatic time is not set, don't bother. Similarly, if we don't
- // have any default network, don't bother.
- if (!isAutomaticTimeRequested() || mDefaultNetwork == null) return;
- mWakeLock.acquire();
- try {
- onPollNetworkTimeUnderWakeLock(event);
- } finally {
- mWakeLock.release();
- }
- }
-
- private void onPollNetworkTimeUnderWakeLock(int event) {
- final long refTime = SystemClock.elapsedRealtime();
- // If NITZ time was received less than mPollingIntervalMs time ago,
- // no need to sync to NTP.
- if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
- resetAlarm(mPollingIntervalMs);
- return;
- }
- final long currentTime = System.currentTimeMillis();
- if (DBG) Log.d(TAG, "System time = " + currentTime);
- // Get the NTP time
- if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
- || event == EVENT_AUTO_TIME_CHANGED) {
- if (DBG) Log.d(TAG, "Before Ntp fetch");
-
- // force refresh NTP cache when outdated
- if (mTime.getCacheAge() >= mPollingIntervalMs) {
- mTime.forceRefresh();
- }
-
- // only update when NTP time is fresh
- if (mTime.getCacheAge() < mPollingIntervalMs) {
- final long ntp = mTime.currentTimeMillis();
- mTryAgainCounter = 0;
- // If the clock is more than N seconds off or this is the first time it's been
- // fetched since boot, set the current time.
- if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
- || mLastNtpFetchTime == NOT_SET) {
- // Set the system time
- if (DBG && mLastNtpFetchTime == NOT_SET
- && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
- Log.d(TAG, "For initial setup, rtc = " + currentTime);
- }
- if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
- // Make sure we don't overflow, since it's going to be converted to an int
- if (ntp / 1000 < Integer.MAX_VALUE) {
- SystemClock.setCurrentTimeMillis(ntp);
- }
- } else {
- if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
- }
- mLastNtpFetchTime = SystemClock.elapsedRealtime();
- } else {
- // Try again shortly
- mTryAgainCounter++;
- if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
- resetAlarm(mPollingIntervalShorterMs);
- } else {
- // Try much later
- mTryAgainCounter = 0;
- resetAlarm(mPollingIntervalMs);
- }
- return;
- }
- }
- resetAlarm(mPollingIntervalMs);
- }
-
- /**
- * Cancel old alarm and starts a new one for the specified interval.
- *
- * @param interval when to trigger the alarm, starting from now.
- */
- private void resetAlarm(long interval) {
- mAlarmManager.cancel(mPendingPollIntent);
- long now = SystemClock.elapsedRealtime();
- long next = now + interval;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
- }
-
- /**
- * Checks if the user prefers to automatically set the time.
- */
- private boolean isAutomaticTimeRequested() {
- return Settings.Global.getInt(
- mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
- }
-
- /** Receiver for Nitz time events */
- private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DBG) Log.d(TAG, "Received " + action);
- if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
- mNitzTimeSetTime = SystemClock.elapsedRealtime();
- }
- }
- };
-
- /** Handler to do the network accesses on */
- private class MyHandler extends Handler {
-
- public MyHandler(Looper l) {
- super(l);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_AUTO_TIME_CHANGED:
- case EVENT_POLL_NETWORK_TIME:
- case EVENT_NETWORK_CHANGED:
- onPollNetworkTime(msg.what);
- break;
- }
- }
- }
-
- private class NetworkTimeUpdateCallback extends NetworkCallback {
- @Override
- public void onAvailable(Network network) {
- Log.d(TAG, String.format("New default network %s; checking time.", network));
- mDefaultNetwork = network;
- // Running on mHandler so invoke directly.
- onPollNetworkTime(EVENT_NETWORK_CHANGED);
- }
-
- @Override
- public void onLost(Network network) {
- if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
- }
- }
-
- /** Observer to watch for changes to the AUTO_TIME setting */
- private static class SettingsObserver extends ContentObserver {
-
- private int mMsg;
- private Handler mHandler;
-
- SettingsObserver(Handler handler, int msg) {
- super(handler);
- mHandler = handler;
- mMsg = msg;
- }
-
- void observe(Context context) {
- ContentResolver resolver = context.getContentResolver();
- resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
- false, this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- mHandler.obtainMessage(mMsg).sendToTarget();
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- pw.print("PollingIntervalMs: ");
- TimeUtils.formatDuration(mPollingIntervalMs, pw);
- pw.print("\nPollingIntervalShorterMs: ");
- TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
- pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
- pw.print("TimeErrorThresholdMs: ");
- TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
- pw.println("\nTryAgainCounter: " + mTryAgainCounter);
- pw.print("LastNtpFetchTime: ");
- TimeUtils.formatDuration(mLastNtpFetchTime, pw);
- pw.println();
- }
+ void systemRunning();
}
diff --git a/services/core/java/com/android/server/NewNetworkTimeUpdateService.java b/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
new file mode 100644
index 0000000..e918c1d
--- /dev/null
+++ b/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010 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;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TimeUtils;
+import android.util.TrustedTime;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class NewNetworkTimeUpdateService extends Binder implements NetworkTimeUpdateService {
+
+ private static final String TAG = "NetworkTimeUpdateService";
+ private static final boolean DBG = false;
+
+ private static final int EVENT_AUTO_TIME_CHANGED = 1;
+ private static final int EVENT_POLL_NETWORK_TIME = 2;
+ private static final int EVENT_NETWORK_CHANGED = 3;
+
+ private static final String ACTION_POLL =
+ "com.android.server.NetworkTimeUpdateService.action.POLL";
+
+ private static final int POLL_REQUEST = 0;
+
+ private static final long NOT_SET = -1;
+ private long mNitzTimeSetTime = NOT_SET;
+ private Network mDefaultNetwork = null;
+
+ private Context mContext;
+ private TrustedTime mTime;
+
+ // NTP lookup is done on this thread and handler
+ private Handler mHandler;
+ private AlarmManager mAlarmManager;
+ private PendingIntent mPendingPollIntent;
+ private SettingsObserver mSettingsObserver;
+ private ConnectivityManager mCM;
+ private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
+ // The last time that we successfully fetched the NTP time.
+ private long mLastNtpFetchTime = NOT_SET;
+ private final PowerManager.WakeLock mWakeLock;
+
+ // Normal polling frequency
+ private final long mPollingIntervalMs;
+ // Try-again polling interval, in case the network request failed
+ private final long mPollingIntervalShorterMs;
+ // Number of times to try again
+ private final int mTryAgainTimesMax;
+ // If the time difference is greater than this threshold, then update the time.
+ private final int mTimeErrorThresholdMs;
+ // Keeps track of how many quick attempts were made to fetch NTP time.
+ // During bootup, the network may not have been up yet, or it's taking time for the
+ // connection to happen.
+ private int mTryAgainCounter;
+
+ public NewNetworkTimeUpdateService(Context context) {
+ mContext = context;
+ mTime = NtpTrustedTime.getInstance(context);
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ Intent pollIntent = new Intent(ACTION_POLL, null);
+ mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+ mPollingIntervalMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingInterval);
+ mPollingIntervalShorterMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingIntervalShorter);
+ mTryAgainTimesMax = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpRetry);
+ mTimeErrorThresholdMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpThreshold);
+
+ mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
+
+ /** Initialize the receivers and initiate the first NTP request */
+ public void systemRunning() {
+ registerForTelephonyIntents();
+ registerForAlarms();
+
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = new MyHandler(thread.getLooper());
+ mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+ mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+ mSettingsObserver.observe(mContext);
+ }
+
+ private void registerForTelephonyIntents() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ mContext.registerReceiver(mNitzReceiver, intentFilter);
+ }
+
+ private void registerForAlarms() {
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+ }
+ }, new IntentFilter(ACTION_POLL));
+ }
+
+ private void onPollNetworkTime(int event) {
+ // If Automatic time is not set, don't bother. Similarly, if we don't
+ // have any default network, don't bother.
+ if (!isAutomaticTimeRequested() || mDefaultNetwork == null) return;
+ mWakeLock.acquire();
+ try {
+ onPollNetworkTimeUnderWakeLock(event);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+
+ private void onPollNetworkTimeUnderWakeLock(int event) {
+ final long refTime = SystemClock.elapsedRealtime();
+ // If NITZ time was received less than mPollingIntervalMs time ago,
+ // no need to sync to NTP.
+ if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
+ resetAlarm(mPollingIntervalMs);
+ return;
+ }
+ final long currentTime = System.currentTimeMillis();
+ if (DBG) Log.d(TAG, "System time = " + currentTime);
+ // Get the NTP time
+ if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
+ || event == EVENT_AUTO_TIME_CHANGED) {
+ if (DBG) Log.d(TAG, "Before Ntp fetch");
+
+ // force refresh NTP cache when outdated
+ if (mTime.getCacheAge() >= mPollingIntervalMs) {
+ mTime.forceRefresh();
+ }
+
+ // only update when NTP time is fresh
+ if (mTime.getCacheAge() < mPollingIntervalMs) {
+ final long ntp = mTime.currentTimeMillis();
+ mTryAgainCounter = 0;
+ // If the clock is more than N seconds off or this is the first time it's been
+ // fetched since boot, set the current time.
+ if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
+ || mLastNtpFetchTime == NOT_SET) {
+ // Set the system time
+ if (DBG && mLastNtpFetchTime == NOT_SET
+ && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
+ Log.d(TAG, "For initial setup, rtc = " + currentTime);
+ }
+ if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
+ // Make sure we don't overflow, since it's going to be converted to an int
+ if (ntp / 1000 < Integer.MAX_VALUE) {
+ SystemClock.setCurrentTimeMillis(ntp);
+ }
+ } else {
+ if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
+ }
+ mLastNtpFetchTime = SystemClock.elapsedRealtime();
+ } else {
+ // Try again shortly
+ mTryAgainCounter++;
+ if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+ resetAlarm(mPollingIntervalShorterMs);
+ } else {
+ // Try much later
+ mTryAgainCounter = 0;
+ resetAlarm(mPollingIntervalMs);
+ }
+ return;
+ }
+ }
+ resetAlarm(mPollingIntervalMs);
+ }
+
+ /**
+ * Cancel old alarm and starts a new one for the specified interval.
+ *
+ * @param interval when to trigger the alarm, starting from now.
+ */
+ private void resetAlarm(long interval) {
+ mAlarmManager.cancel(mPendingPollIntent);
+ long now = SystemClock.elapsedRealtime();
+ long next = now + interval;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+ }
+
+ /**
+ * Checks if the user prefers to automatically set the time.
+ */
+ private boolean isAutomaticTimeRequested() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
+ }
+
+ /** Receiver for Nitz time events */
+ private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (DBG) Log.d(TAG, "Received " + action);
+ if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+ mNitzTimeSetTime = SystemClock.elapsedRealtime();
+ }
+ }
+ };
+
+ /** Handler to do the network accesses on */
+ private class MyHandler extends Handler {
+
+ public MyHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_AUTO_TIME_CHANGED:
+ case EVENT_POLL_NETWORK_TIME:
+ case EVENT_NETWORK_CHANGED:
+ onPollNetworkTime(msg.what);
+ break;
+ }
+ }
+ }
+
+ private class NetworkTimeUpdateCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ Log.d(TAG, String.format("New default network %s; checking time.", network));
+ mDefaultNetwork = network;
+ // Running on mHandler so invoke directly.
+ onPollNetworkTime(EVENT_NETWORK_CHANGED);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+ }
+ }
+
+ /** Observer to watch for changes to the AUTO_TIME setting */
+ private static class SettingsObserver extends ContentObserver {
+
+ private int mMsg;
+ private Handler mHandler;
+
+ SettingsObserver(Handler handler, int msg) {
+ super(handler);
+ mHandler = handler;
+ mMsg = msg;
+ }
+
+ void observe(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
+ false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mMsg).sendToTarget();
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.print("PollingIntervalMs: ");
+ TimeUtils.formatDuration(mPollingIntervalMs, pw);
+ pw.print("\nPollingIntervalShorterMs: ");
+ TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
+ pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
+ pw.print("TimeErrorThresholdMs: ");
+ TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
+ pw.println("\nTryAgainCounter: " + mTryAgainCounter);
+ pw.print("LastNtpFetchTime: ");
+ TimeUtils.formatDuration(mLastNtpFetchTime, pw);
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/OldNetworkTimeUpdateService.java b/services/core/java/com/android/server/OldNetworkTimeUpdateService.java
new file mode 100644
index 0000000..d127b67
--- /dev/null
+++ b/services/core/java/com/android/server/OldNetworkTimeUpdateService.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010 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;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TimeUtils;
+import android.util.TrustedTime;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class OldNetworkTimeUpdateService extends Binder implements NetworkTimeUpdateService {
+
+ private static final String TAG = "NetworkTimeUpdateService";
+ private static final boolean DBG = false;
+
+ private static final int EVENT_AUTO_TIME_CHANGED = 1;
+ private static final int EVENT_POLL_NETWORK_TIME = 2;
+ private static final int EVENT_NETWORK_CHANGED = 3;
+
+ private static final String ACTION_POLL =
+ "com.android.server.NetworkTimeUpdateService.action.POLL";
+
+ private static final int POLL_REQUEST = 0;
+
+ private static final long NOT_SET = -1;
+ private long mNitzTimeSetTime = NOT_SET;
+ private Network mDefaultNetwork = null;
+
+ private Context mContext;
+ private TrustedTime mTime;
+
+ // NTP lookup is done on this thread and handler
+ private Handler mHandler;
+ private AlarmManager mAlarmManager;
+ private PendingIntent mPendingPollIntent;
+ private SettingsObserver mSettingsObserver;
+ private ConnectivityManager mCM;
+ private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
+ // The last time that we successfully fetched the NTP time.
+ private long mLastNtpFetchTime = NOT_SET;
+ private final PowerManager.WakeLock mWakeLock;
+
+ // Normal polling frequency
+ private final long mPollingIntervalMs;
+ // Try-again polling interval, in case the network request failed
+ private final long mPollingIntervalShorterMs;
+ // Number of times to try again
+ private final int mTryAgainTimesMax;
+ // If the time difference is greater than this threshold, then update the time.
+ private final int mTimeErrorThresholdMs;
+ // Keeps track of how many quick attempts were made to fetch NTP time.
+ // During bootup, the network may not have been up yet, or it's taking time for the
+ // connection to happen.
+ private int mTryAgainCounter;
+
+ public OldNetworkTimeUpdateService(Context context) {
+ mContext = context;
+ mTime = NtpTrustedTime.getInstance(context);
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ Intent pollIntent = new Intent(ACTION_POLL, null);
+ mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+ mPollingIntervalMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingInterval);
+ mPollingIntervalShorterMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingIntervalShorter);
+ mTryAgainTimesMax = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpRetry);
+ mTimeErrorThresholdMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpThreshold);
+
+ mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
+
+ /** Initialize the receivers and initiate the first NTP request */
+ public void systemRunning() {
+ registerForTelephonyIntents();
+ registerForAlarms();
+
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = new MyHandler(thread.getLooper());
+ mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+ mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+ mSettingsObserver.observe(mContext);
+ }
+
+ private void registerForTelephonyIntents() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ mContext.registerReceiver(mNitzReceiver, intentFilter);
+ }
+
+ private void registerForAlarms() {
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+ }
+ }, new IntentFilter(ACTION_POLL));
+ }
+
+ private void onPollNetworkTime(int event) {
+ // If Automatic time is not set, don't bother. Similarly, if we don't
+ // have any default network, don't bother.
+ if (!isAutomaticTimeRequested() || mDefaultNetwork == null) return;
+ mWakeLock.acquire();
+ try {
+ onPollNetworkTimeUnderWakeLock(event);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+
+ private void onPollNetworkTimeUnderWakeLock(int event) {
+ final long refTime = SystemClock.elapsedRealtime();
+ // If NITZ time was received less than mPollingIntervalMs time ago,
+ // no need to sync to NTP.
+ if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
+ resetAlarm(mPollingIntervalMs);
+ return;
+ }
+ final long currentTime = System.currentTimeMillis();
+ if (DBG) Log.d(TAG, "System time = " + currentTime);
+ // Get the NTP time
+ if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
+ || event == EVENT_AUTO_TIME_CHANGED) {
+ if (DBG) Log.d(TAG, "Before Ntp fetch");
+
+ // force refresh NTP cache when outdated
+ if (mTime.getCacheAge() >= mPollingIntervalMs) {
+ mTime.forceRefresh();
+ }
+
+ // only update when NTP time is fresh
+ if (mTime.getCacheAge() < mPollingIntervalMs) {
+ final long ntp = mTime.currentTimeMillis();
+ mTryAgainCounter = 0;
+ // If the clock is more than N seconds off or this is the first time it's been
+ // fetched since boot, set the current time.
+ if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
+ || mLastNtpFetchTime == NOT_SET) {
+ // Set the system time
+ if (DBG && mLastNtpFetchTime == NOT_SET
+ && Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
+ Log.d(TAG, "For initial setup, rtc = " + currentTime);
+ }
+ if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
+ // Make sure we don't overflow, since it's going to be converted to an int
+ if (ntp / 1000 < Integer.MAX_VALUE) {
+ SystemClock.setCurrentTimeMillis(ntp);
+ }
+ } else {
+ if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
+ }
+ mLastNtpFetchTime = SystemClock.elapsedRealtime();
+ } else {
+ // Try again shortly
+ mTryAgainCounter++;
+ if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+ resetAlarm(mPollingIntervalShorterMs);
+ } else {
+ // Try much later
+ mTryAgainCounter = 0;
+ resetAlarm(mPollingIntervalMs);
+ }
+ return;
+ }
+ }
+ resetAlarm(mPollingIntervalMs);
+ }
+
+ /**
+ * Cancel old alarm and starts a new one for the specified interval.
+ *
+ * @param interval when to trigger the alarm, starting from now.
+ */
+ private void resetAlarm(long interval) {
+ mAlarmManager.cancel(mPendingPollIntent);
+ long now = SystemClock.elapsedRealtime();
+ long next = now + interval;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+ }
+
+ /**
+ * Checks if the user prefers to automatically set the time.
+ */
+ private boolean isAutomaticTimeRequested() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
+ }
+
+ /** Receiver for Nitz time events */
+ private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (DBG) Log.d(TAG, "Received " + action);
+ if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+ mNitzTimeSetTime = SystemClock.elapsedRealtime();
+ }
+ }
+ };
+
+ /** Handler to do the network accesses on */
+ private class MyHandler extends Handler {
+
+ public MyHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_AUTO_TIME_CHANGED:
+ case EVENT_POLL_NETWORK_TIME:
+ case EVENT_NETWORK_CHANGED:
+ onPollNetworkTime(msg.what);
+ break;
+ }
+ }
+ }
+
+ private class NetworkTimeUpdateCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ Log.d(TAG, String.format("New default network %s; checking time.", network));
+ mDefaultNetwork = network;
+ // Running on mHandler so invoke directly.
+ onPollNetworkTime(EVENT_NETWORK_CHANGED);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+ }
+ }
+
+ /** Observer to watch for changes to the AUTO_TIME setting */
+ private static class SettingsObserver extends ContentObserver {
+
+ private int mMsg;
+ private Handler mHandler;
+
+ SettingsObserver(Handler handler, int msg) {
+ super(handler);
+ mHandler = handler;
+ mMsg = msg;
+ }
+
+ void observe(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
+ false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mMsg).sendToTarget();
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.print("PollingIntervalMs: ");
+ TimeUtils.formatDuration(mPollingIntervalMs, pw);
+ pw.print("\nPollingIntervalShorterMs: ");
+ TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
+ pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
+ pw.print("TimeErrorThresholdMs: ");
+ TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
+ pw.println("\nTryAgainCounter: " + mTryAgainCounter);
+ pw.print("LastNtpFetchTime: ");
+ TimeUtils.formatDuration(mLastNtpFetchTime, pw);
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index bce735b..2a80f0e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -115,6 +115,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -895,6 +896,42 @@
.compareTo(MOST_IPV6_ADDRESSES_COUNT) >= 0;
}
+ /**
+ * Attempt to perform a seamless handover of VPNs by only updating LinkProperties without
+ * registering a new NetworkAgent. This is not always possible if the new VPN configuration
+ * has certain changes, in which case this method would just return {@code false}.
+ */
+ private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
+ // NetworkMisc cannot be updated without registering a new NetworkAgent.
+ if (oldConfig.allowBypass != mConfig.allowBypass) {
+ Log.i(TAG, "Handover not possible due to changes to allowBypass");
+ return false;
+ }
+
+ // TODO: we currently do not support seamless handover if the allowed or disallowed
+ // applications have changed. Consider diffing UID ranges and only applying the delta.
+ if (!Objects.equals(oldConfig.allowedApplications, mConfig.allowedApplications) ||
+ !Objects.equals(oldConfig.disallowedApplications, mConfig.disallowedApplications)) {
+ Log.i(TAG, "Handover not possible due to changes to whitelisted/blacklisted apps");
+ return false;
+ }
+
+ LinkProperties lp = makeLinkProperties();
+ final boolean hadInternetCapability = mNetworkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ final boolean willHaveInternetCapability = providesRoutesToMostDestinations(lp);
+ if (hadInternetCapability != willHaveInternetCapability) {
+ // A seamless handover would have led to a change to INTERNET capability, which
+ // is supposed to be immutable for a given network. In this case bail out and do not
+ // perform handover.
+ Log.i(TAG, "Handover not possible due to changes to INTERNET capability");
+ return false;
+ }
+
+ agent.sendLinkProperties(lp);
+ return true;
+ }
+
private void agentConnect() {
LinkProperties lp = makeLinkProperties();
@@ -1003,13 +1040,11 @@
String oldInterface = mInterface;
Connection oldConnection = mConnection;
NetworkAgent oldNetworkAgent = mNetworkAgent;
- mNetworkAgent = null;
Set<UidRange> oldUsers = mNetworkCapabilities.getUids();
// Configure the interface. Abort if any of these steps fails.
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
try {
- updateState(DetailedState.CONNECTING, "establish");
String interfaze = jniGetName(tun.getFd());
// TEMP use the old jni calls until there is support for netd address setting
@@ -1037,15 +1072,26 @@
mConfig = config;
// Set up forwarding and DNS rules.
- agentConnect();
+ // First attempt to do a seamless handover that only changes the interface name and
+ // parameters. If that fails, disconnect.
+ if (oldConfig != null
+ && updateLinkPropertiesInPlaceIfPossible(mNetworkAgent, oldConfig)) {
+ // Keep mNetworkAgent unchanged
+ } else {
+ mNetworkAgent = null;
+ updateState(DetailedState.CONNECTING, "establish");
+ // Set up forwarding and DNS rules.
+ agentConnect();
+ // Remove the old tun's user forwarding rules
+ // The new tun's user rules have already been added above so they will take over
+ // as rules are deleted. This prevents data leakage as the rules are moved over.
+ agentDisconnect(oldNetworkAgent);
+ }
if (oldConnection != null) {
mContext.unbindService(oldConnection);
}
- // Remove the old tun's user forwarding rules
- // The new tun's user rules have already been added so they will take over
- // as rules are deleted. This prevents data leakage as the rules are moved over.
- agentDisconnect(oldNetworkAgent);
+
if (oldInterface != null && !oldInterface.equals(interfaze)) {
jniReset(oldInterface);
}
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index e5207cb..7bdc8a3 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -20,38 +20,213 @@
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.timedetector.TimeSignal;
+import android.content.Intent;
import android.util.Slog;
+import android.util.TimestampedValue;
-import java.io.FileDescriptor;
+import com.android.internal.telephony.TelephonyIntents;
+
import java.io.PrintWriter;
/**
- * A placeholder implementation of TimeDetectorStrategy that passes NITZ suggestions immediately
- * to {@link AlarmManager}.
+ * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
+ * {@link AlarmManager}. The TimeDetectorService handles thread safety: all calls to
+ * this class can be assumed to be single threaded (though the thread used may vary).
*/
+// @NotThreadSafe
public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
private final static String TAG = "timedetector.SimpleTimeDetectorStrategy";
- private Callback mHelper;
+ /**
+ * 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.
+ */
+ private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
+
+ // @NonNull after initialize()
+ private Callback mCallback;
+
+ // NITZ state.
+ @Nullable private TimestampedValue<Long> mLastNitzTime;
+
+
+ // Information about the last time signal received: Used when toggling auto-time.
+ @Nullable private TimestampedValue<Long> mLastSystemClockTime;
+ private boolean mLastSystemClockTimeSendNetworkBroadcast;
+
+ // System clock state.
+ @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet;
@Override
public void initialize(@NonNull Callback callback) {
- mHelper = callback;
+ mCallback = 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);
+ Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal);
return;
}
- mHelper.setTime(timeSignal.getUtcTime());
+ // NITZ logic
+
+ TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime();
+ boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime);
+ if (!nitzTimeIsValid) {
+ 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.
+ mLastNitzTime = newNitzUtcTime;
+
+ // System clock update logic.
+
+ // Historically, Android has sent a telephony broadcast only when setting the time using
+ // NITZ.
+ final boolean sendNetworkBroadcast =
+ TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId());
+
+ final TimestampedValue<Long> newUtcTime = newNitzUtcTime;
+ setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
+ }
+
+ private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime,
+ TimestampedValue<Long> lastNitzTime) {
+
+ if (lastNitzTime != null) {
+ long referenceTimeDifference =
+ TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime);
+ if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
+ // Out of order or bogus.
+ Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
+ + " referenceTimeDifference=" + referenceTimeDifference
+ + " lastNitzTime=" + lastNitzTime
+ + " newNitzUtcTime=" + newNitzUtcTime);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ 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);
+ return;
+ }
+
+ mCallback.acquireWakeLock();
+ try {
+ long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long actualTimeMillis = mCallback.systemClockMillis();
+
+ // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
+ // may be setting the clock.
+ if (mLastSystemClockTimeSet != null) {
+ long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
+ mLastSystemClockTimeSet, 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."
+ + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+ + " expectedTimeMillis=" + expectedTimeMillis
+ + " actualTimeMillis=" + actualTimeMillis);
+ }
+ }
+
+ final String reason = "New time signal";
+ adjustAndSetDeviceSystemClock(
+ time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason);
+ } finally {
+ mCallback.releaseWakeLock();
+ }
}
@Override
- public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) {
- // No state to dump.
+ 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) {
+ // Only send the network broadcast if the last candidate would have caused one.
+ final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast;
+
+ mCallback.acquireWakeLock();
+ try {
+ long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long actualTimeMillis = mCallback.systemClockMillis();
+
+ final String reason = "Automatic time detection enabled.";
+ adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast,
+ elapsedRealtimeMillis, actualTimeMillis, reason);
+ } finally {
+ mCallback.releaseWakeLock();
+ }
+ }
+ } else {
+ // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
+ // it should be in future.
+ mLastSystemClockTimeSet = null;
+ }
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
+ pw.println("mLastNitzTime=" + mLastNitzTime);
+ pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet);
+ pw.println("mLastSystemClockTime=" + mLastSystemClockTime);
+ pw.println("mLastSystemClockTimeSendNetworkBroadcast="
+ + mLastSystemClockTimeSendNetworkBroadcast);
+ }
+
+ private void adjustAndSetDeviceSystemClock(
+ TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
+ long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) {
+
+ // Adjust for the time that has elapsed since the signal was received.
+ long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
+
+ // Check if the new signal would make sufficient difference to the system clock. If it's
+ // below the threshold then ignore it.
+ long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
+ long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
+ if (absTimeDifference < systemClockUpdateThreshold) {
+ Slog.d(TAG, "adjustAndSetDeviceSystemClock: Not setting system clock. New time and"
+ + " system clock are close enough."
+ + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+ + " newTime=" + newTime
+ + " reason=" + reason
+ + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
+ + " absTimeDifference=" + absTimeDifference);
+ return;
+ }
+
+ Slog.d(TAG, "Setting system clock using time=" + newTime
+ + " reason=" + reason
+ + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+ + " newTimeMillis=" + newSystemClockMillis);
+ mCallback.setSystemClock(newSystemClockMillis);
+
+ // CLOCK_PARANOIA : Record the last time this class set the system clock.
+ mLastSystemClockTimeSet = newTime;
+
+ if (sendNetworkBroadcast) {
+ // Send a broadcast that telephony code used to send after setting the clock.
+ // TODO Remove this broadcast as soon as there are no remaining listeners.
+ Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time", newSystemClockMillis);
+ mCallback.sendStickyBroadcast(intent);
+ }
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 0ec24d8..9c83000 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -20,24 +20,29 @@
import android.annotation.Nullable;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.TimeSignal;
+import android.content.ContentResolver;
import android.content.Context;
+import android.database.ContentObserver;
import android.os.Binder;
+import android.provider.Settings;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
import com.android.server.SystemService;
+import com.android.server.timedetector.TimeDetectorStrategy.Callback;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
public final class TimeDetectorService extends ITimeDetectorService.Stub {
-
private static final String TAG = "timedetector.TimeDetectorService";
public static class Lifecycle extends SystemService {
- public Lifecycle(Context context) {
+ public Lifecycle(@NonNull Context context) {
super(context);
}
@@ -51,31 +56,65 @@
}
}
- private final Context mContext;
- private final TimeDetectorStrategy mTimeDetectorStrategy;
+ @NonNull private final Context mContext;
+ @NonNull private final Callback mCallback;
- private static TimeDetectorService create(Context context) {
- TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
- timeDetector.initialize(new TimeDetectorStrategyCallbackImpl(context));
- return new TimeDetectorService(context, timeDetector);
+ // The lock used when call the strategy to ensure thread safety.
+ @NonNull private final Object mStrategyLock = new Object();
+
+ @GuardedBy("mStrategyLock")
+ @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
+
+ private static TimeDetectorService create(@NonNull Context context) {
+ final TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
+ final TimeDetectorStrategyCallbackImpl callback =
+ new TimeDetectorStrategyCallbackImpl(context);
+ timeDetector.initialize(callback);
+
+ TimeDetectorService timeDetectorService =
+ new TimeDetectorService(context, callback, timeDetector);
+
+ // Wire up event listening.
+ ContentResolver contentResolver = context.getContentResolver();
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
+ new ContentObserver(FgThread.getHandler()) {
+ public void onChange(boolean selfChange) {
+ timeDetectorService.handleAutoTimeDetectionToggle();
+ }
+ });
+
+ return timeDetectorService;
}
@VisibleForTesting
- public TimeDetectorService(@NonNull Context context,
+ public TimeDetectorService(@NonNull Context context, @NonNull Callback callback,
@NonNull TimeDetectorStrategy timeDetectorStrategy) {
mContext = Objects.requireNonNull(context);
+ mCallback = Objects.requireNonNull(callback);
mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
}
@Override
public void suggestTime(@NonNull TimeSignal timeSignal) {
enforceSetTimePermission();
+ Objects.requireNonNull(timeSignal);
- long callerIdToken = Binder.clearCallingIdentity();
+ long idToken = Binder.clearCallingIdentity();
try {
- mTimeDetectorStrategy.suggestTime(timeSignal);
+ synchronized (mStrategyLock) {
+ mTimeDetectorStrategy.suggestTime(timeSignal);
+ }
} finally {
- Binder.restoreCallingIdentity(callerIdToken);
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+
+ @VisibleForTesting
+ public void handleAutoTimeDetectionToggle() {
+ synchronized (mStrategyLock) {
+ final boolean timeDetectionEnabled = mCallback.isTimeDetectionEnabled();
+ mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled);
}
}
@@ -84,7 +123,9 @@
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- mTimeDetectorStrategy.dump(fd, pw, args);
+ synchronized (mStrategyLock) {
+ mTimeDetectorStrategy.dump(pw, args);
+ }
}
private void enforceSetTimePermission() {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 5cb2eed..e050865 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -19,26 +19,66 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timedetector.TimeSignal;
+import android.content.Intent;
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.
+ * TimeDetectorService. The TimeDetectorService handles thread safety: all calls to implementations
+ * of this interface can be assumed to be single threaded (though the thread used may vary).
*
* @hide
*/
+// @NotThreadSafe
public interface TimeDetectorStrategy {
+ /**
+ * The interface used by the strategy to interact with the surrounding service.
+ */
interface Callback {
- void setTime(TimestampedValue<Long> time);
+
+ /**
+ * The absolute threshold below which the system clock need not be updated. i.e. if setting
+ * the system clock would adjust it by less than this (either backwards or forwards) then it
+ * need not be set.
+ */
+ int systemClockUpdateThresholdMillis();
+
+ /** Returns true if automatic time detection is enabled. */
+ boolean isTimeDetectionEnabled();
+
+ /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */
+ void acquireWakeLock();
+
+ /** Returns the elapsedRealtimeMillis clock value. The WakeLock must be held. */
+ long elapsedRealtimeMillis();
+
+ /** Returns the system clock value. The WakeLock must be held. */
+ long systemClockMillis();
+
+ /** Sets the device system clock. The WakeLock must be held. */
+ void setSystemClock(long newTimeMillis);
+
+ /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */
+ void releaseWakeLock();
+
+ /** Send the supplied intent as a stick broadcast. */
+ void sendStickyBroadcast(@NonNull Intent intent);
}
+ /** Initialize the strategy. */
void initialize(@NonNull Callback callback);
+
+ /** Process the suggested time. */
void suggestTime(@NonNull TimeSignal timeSignal);
- void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args);
+
+ /** Handle the auto-time setting being toggled on or off. */
+ void handleAutoTimeDetectionToggle(boolean enabled);
+
+ /** Dump debug information. */
+ void dump(@NonNull PrintWriter pw, @Nullable String[] args);
// Utility methods below are to be moved to a better home when one becomes more obvious.
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
index 568d73a..77b9e62 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
@@ -18,41 +18,108 @@
import android.annotation.NonNull;
import android.app.AlarmManager;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
-import android.util.TimestampedValue;
+
+import java.util.Objects;
/**
* The real implementation of {@link TimeDetectorStrategy.Callback} used on device.
*/
-public class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategy.Callback {
+public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategy.Callback {
private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl";
- @NonNull private PowerManager.WakeLock mWakeLock;
- @NonNull private AlarmManager mAlarmManager;
+ private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
- public TimeDetectorStrategyCallbackImpl(Context context) {
+ /**
+ * If a newly calculated system clock time and the current system clock time differs by this or
+ * more the system clock will actually be updated. Used to prevent the system clock being set
+ * for only minor differences.
+ */
+ private final int mSystemClockUpdateThresholdMillis;
+
+ @NonNull private final Context mContext;
+ @NonNull private final ContentResolver mContentResolver;
+ @NonNull private final PowerManager.WakeLock mWakeLock;
+ @NonNull private final AlarmManager mAlarmManager;
+
+ public TimeDetectorStrategyCallbackImpl(@NonNull Context context) {
+ mContext = Objects.requireNonNull(context);
+ mContentResolver = Objects.requireNonNull(context.getContentResolver());
+
PowerManager powerManager = context.getSystemService(PowerManager.class);
+ mWakeLock = Objects.requireNonNull(
+ powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG));
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class));
- mAlarmManager = context.getSystemService(AlarmManager.class);
+ mSystemClockUpdateThresholdMillis =
+ SystemProperties.getInt("ro.sys.time_detector_update_diff",
+ SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
}
@Override
- public void setTime(TimestampedValue<Long> time) {
- mWakeLock.acquire();
+ public int systemClockUpdateThresholdMillis() {
+ return mSystemClockUpdateThresholdMillis;
+ }
+
+ @Override
+ public boolean isTimeDetectionEnabled() {
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();
+ return Settings.Global.getInt(mContentResolver, Settings.Global.AUTO_TIME) != 0;
+ } catch (Settings.SettingNotFoundException snfe) {
+ return true;
+ }
+ }
+
+ @Override
+ public void acquireWakeLock() {
+ if (mWakeLock.isHeld()) {
+ Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held");
+ }
+ mWakeLock.acquire();
+ }
+
+ @Override
+ public long elapsedRealtimeMillis() {
+ checkWakeLockHeld();
+ return SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public long systemClockMillis() {
+ checkWakeLockHeld();
+ return System.currentTimeMillis();
+ }
+
+ @Override
+ public void setSystemClock(long newTimeMillis) {
+ checkWakeLockHeld();
+ mAlarmManager.setTime(newTimeMillis);
+ }
+
+ @Override
+ public void releaseWakeLock() {
+ checkWakeLockHeld();
+ mWakeLock.release();
+ }
+
+ @Override
+ public void sendStickyBroadcast(@NonNull Intent intent) {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void checkWakeLockHeld() {
+ if (!mWakeLock.isHeld()) {
+ Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held");
}
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
new file mode 100644
index 0000000..5f71b0b
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -0,0 +1,67 @@
+/*
+ * 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.timezonedetector;
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+import android.app.timezonedetector.ITimeZoneDetectorService;
+import android.content.Context;
+import android.util.Slog;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
+ private static final String TAG = "timezonedetector.TimeZoneDetectorService";
+
+ public static class Lifecycle extends SystemService {
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
+ // Publish the binder service so it can be accessed from other (appropriately
+ // permissioned) processes.
+ publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
+ }
+ }
+
+ private final Context mContext;
+
+ private static TimeZoneDetectorService create(Context context) {
+ return new TimeZoneDetectorService(context);
+ }
+
+ public TimeZoneDetectorService(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void stubbedCall() {
+ // Empty call for initial tests.
+ Slog.d(TAG, "stubbedCall() called");
+ // TODO: Remove when there are real methods.
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ // TODO: Implement when there is state.
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 58e77e8..a602ab1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -206,6 +206,8 @@
"com.android.server.timezone.RulesManagerService$Lifecycle";
private static final String TIME_DETECTOR_SERVICE_CLASS =
"com.android.server.timedetector.TimeDetectorService$Lifecycle";
+ private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
+ "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -1203,13 +1205,24 @@
traceEnd();
}
- traceBeginAndSlog("StartTimeDetectorService");
- try {
- mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting StartTimeDetectorService service", e);
+ final boolean useNewTimeServices = true;
+ if (useNewTimeServices) {
+ traceBeginAndSlog("StartTimeDetectorService");
+ try {
+ mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting StartTimeDetectorService service", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartTimeZoneDetectorService");
+ try {
+ mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting StartTimeZoneDetectorService service", e);
+ }
+ traceEnd();
}
- traceEnd();
if (!disableNonCoreServices && !disableSearchManager) {
traceBeginAndSlog("StartSearchManagerService");
@@ -1384,7 +1397,12 @@
if (!disableNetwork && !disableNetworkTime) {
traceBeginAndSlog("StartNetworkTimeUpdateService");
try {
- networkTimeUpdater = new NetworkTimeUpdateService(context);
+ if (useNewTimeServices) {
+ networkTimeUpdater = new NewNetworkTimeUpdateService(context);
+ } else {
+ networkTimeUpdater = new OldNetworkTimeUpdateService(context);
+ }
+ Slog.d(TAG, "Using networkTimeUpdater class=" + networkTimeUpdater.getClass());
ServiceManager.addService("network_time_update_service", networkTimeUpdater);
} catch (Throwable e) {
reportWtf("starting NetworkTimeUpdate service", e);
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
index e4b3b13..62f1433 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
@@ -16,12 +16,18 @@
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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.timedetector.TimeSignal;
+import android.content.Intent;
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
import android.support.test.runner.AndroidJUnit4;
import android.util.TimestampedValue;
@@ -32,37 +38,476 @@
@RunWith(AndroidJUnit4.class)
public class SimpleTimeZoneDetectorStrategyTest {
- private TimeDetectorStrategy.Callback mMockCallback;
+ private static final Scenario SCENARIO_1 = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .build();
- private SimpleTimeDetectorStrategy mSimpleTimeZoneDetectorStrategy;
+ private Script mScript;
@Before
public void setUp() {
- mMockCallback = mock(TimeDetectorStrategy.Callback.class);
- mSimpleTimeZoneDetectorStrategy = new SimpleTimeDetectorStrategy();
- mSimpleTimeZoneDetectorStrategy.initialize(mMockCallback);
+ mScript = new Script();
}
@Test
- public void testSuggestTime_nitz() {
- TimestampedValue<Long> utcTime = createUtcTime();
- TimeSignal timeSignal = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime);
+ public void testSuggestTime_nitz_timeDetectionEnabled() {
+ Scenario scenario = SCENARIO_1;
+ mScript.pokeFakeClocks(scenario)
+ .pokeTimeDetectionEnabled(true);
- mSimpleTimeZoneDetectorStrategy.suggestTime(timeSignal);
+ TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ final int clockIncrement = 1000;
+ long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
- verify(mMockCallback).setTime(utcTime);
+ mScript.simulateTimePassing(clockIncrement)
+ .simulateTimeSignalReceived(timeSignal)
+ .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis);
+ }
+
+ @Test
+ public void testSuggestTime_systemClockThreshold() {
+ Scenario scenario = SCENARIO_1;
+ final int systemClockUpdateThresholdMillis = 1000;
+ mScript.pokeFakeClocks(scenario)
+ .pokeThresholds(systemClockUpdateThresholdMillis)
+ .pokeTimeDetectionEnabled(true);
+
+ TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+ final int clockIncrement = 100;
+ // Increment the the device clocks to simulate the passage of time.
+ mScript.simulateTimePassing(clockIncrement);
+
+ long expectSystemClockMillis1 =
+ TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+ // Send the first time signal. It should be used.
+ mScript.simulateTimeSignalReceived(timeSignal1)
+ .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis1);
+
+ // Now send another time signal, but one that is too similar to the last one and should be
+ // ignored.
+ int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
+ TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+ mScript.peekElapsedRealtimeMillis(),
+ mScript.peekSystemClockMillis() + underThresholdMillis);
+ TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+ mScript.simulateTimePassing(clockIncrement)
+ .simulateTimeSignalReceived(timeSignal2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Now send another time signal, but one that is on the threshold and so should be used.
+ TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
+ mScript.peekElapsedRealtimeMillis(),
+ mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
+
+ TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
+ mScript.simulateTimePassing(clockIncrement);
+
+ long expectSystemClockMillis3 =
+ TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis());
+
+ mScript.simulateTimeSignalReceived(timeSignal3)
+ .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis3);
+ }
+
+ @Test
+ public void testSuggestTime_nitz_timeDetectionDisabled() {
+ Scenario scenario = SCENARIO_1;
+ mScript.pokeFakeClocks(scenario)
+ .pokeTimeDetectionEnabled(false);
+
+ TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ mScript.simulateTimeSignalReceived(timeSignal)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+ }
+
+ @Test
+ public void testSuggestTime_nitz_invalidNitzReferenceTimesIgnored() {
+ Scenario scenario = SCENARIO_1;
+ final int systemClockUpdateThreshold = 2000;
+ mScript.pokeFakeClocks(scenario)
+ .pokeThresholds(systemClockUpdateThreshold)
+ .pokeTimeDetectionEnabled(true);
+ TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+ // Initialize the strategy / device with a time set from NITZ.
+ mScript.simulateTimePassing(100);
+ long expectedSystemClockMillis1 =
+ TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+ mScript.simulateTimeSignalReceived(timeSignal1)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
+
+ // The UTC time increment should be larger than the system clock update threshold so we
+ // know it shouldn't be ignored for other reasons.
+ long validUtcTimeMillis = utcTime1.getValue() + (2 * systemClockUpdateThreshold);
+
+ // Now supply a new signal that has an obviously bogus reference time : older than the last
+ // one.
+ long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
+ TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+ referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
+ TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+ mScript.simulateTimeSignalReceived(timeSignal2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Now supply a new signal that has an obviously bogus reference time : substantially in the
+ // future.
+ long referenceTimeInFutureMillis =
+ utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
+ TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
+ referenceTimeInFutureMillis, validUtcTimeMillis);
+ TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
+ mScript.simulateTimeSignalReceived(timeSignal3)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Just to prove validUtcTimeMillis is valid.
+ long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
+ TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
+ validReferenceTimeMillis, validUtcTimeMillis);
+ long expectedSystemClockMillis4 =
+ TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
+ TimeSignal timeSignal4 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime4);
+ mScript.simulateTimeSignalReceived(timeSignal4)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4);
+ }
+
+ @Test
+ public void testSuggestTime_timeDetectionToggled() {
+ Scenario scenario = SCENARIO_1;
+ final int clockIncrementMillis = 100;
+ final int systemClockUpdateThreshold = 2000;
+ mScript.pokeFakeClocks(scenario)
+ .pokeThresholds(systemClockUpdateThreshold)
+ .pokeTimeDetectionEnabled(false);
+
+ TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+ // Simulate time passing.
+ mScript.simulateTimePassing(clockIncrementMillis);
+
+ // Simulate the time signal being received. It should not be used because auto time
+ // detection is off but it should be recorded.
+ mScript.simulateTimeSignalReceived(timeSignal1)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Simulate more time passing.
+ mScript.simulateTimePassing(clockIncrementMillis);
+
+ long expectedSystemClockMillis1 =
+ TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+ // Turn on auto time detection.
+ mScript.simulateAutoTimeDetectionToggle()
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
+
+ // Turn off auto time detection.
+ mScript.simulateAutoTimeDetectionToggle()
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Receive another valid time signal.
+ // It should be on the threshold and accounting for the clock increments.
+ TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+ mScript.peekElapsedRealtimeMillis(),
+ mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
+ TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+
+ // Simulate more time passing.
+ mScript.simulateTimePassing(clockIncrementMillis);
+
+ long expectedSystemClockMillis2 =
+ TimeDetectorStrategy.getTimeAt(utcTime2, mScript.peekElapsedRealtimeMillis());
+
+ // The new time, though valid, should not be set in the system clock because auto time is
+ // disabled.
+ mScript.simulateTimeSignalReceived(timeSignal2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Turn on auto time detection.
+ mScript.simulateAutoTimeDetectionToggle()
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2);
}
@Test
public void testSuggestTime_unknownSource() {
- TimestampedValue<Long> utcTime = createUtcTime();
- TimeSignal timeSignal = new TimeSignal("unknown", utcTime);
- mSimpleTimeZoneDetectorStrategy.suggestTime(timeSignal);
+ Scenario scenario = SCENARIO_1;
+ mScript.pokeFakeClocks(scenario)
+ .pokeTimeDetectionEnabled(true);
- verify(mMockCallback, never()).setTime(any());
+ TimeSignal timeSignal = scenario.createTimeSignalForActual("unknown");
+ mScript.simulateTimeSignalReceived(timeSignal)
+ .verifySystemClockWasNotSetAndResetCallTracking();
}
- private static TimestampedValue<Long> createUtcTime() {
- return new TimestampedValue<>(321L, 123456L);
+ /**
+ * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
+ * like the real thing should, it also asserts preconditions.
+ */
+ private static class FakeCallback implements TimeDetectorStrategy.Callback {
+ private boolean mTimeDetectionEnabled;
+ private boolean mWakeLockAcquired;
+ private long mElapsedRealtimeMillis;
+ private long mSystemClockMillis;
+ private int mSystemClockUpdateThresholdMillis = 2000;
+
+ // Tracking operations.
+ private boolean mSystemClockWasSet;
+ private Intent mBroadcastSent;
+
+ @Override
+ public int systemClockUpdateThresholdMillis() {
+ return mSystemClockUpdateThresholdMillis;
+ }
+
+ @Override
+ public boolean isTimeDetectionEnabled() {
+ return mTimeDetectionEnabled;
+ }
+
+ @Override
+ public void acquireWakeLock() {
+ if (mWakeLockAcquired) {
+ fail("Wake lock already acquired");
+ }
+ mWakeLockAcquired = true;
+ }
+
+ @Override
+ public long elapsedRealtimeMillis() {
+ assertWakeLockAcquired();
+ return mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public long systemClockMillis() {
+ assertWakeLockAcquired();
+ return mSystemClockMillis;
+ }
+
+ @Override
+ public void setSystemClock(long newTimeMillis) {
+ assertWakeLockAcquired();
+ mSystemClockWasSet = true;
+ mSystemClockMillis = newTimeMillis;
+ }
+
+ @Override
+ public void releaseWakeLock() {
+ assertWakeLockAcquired();
+ mWakeLockAcquired = false;
+ }
+
+ @Override
+ public void sendStickyBroadcast(Intent intent) {
+ assertNotNull(intent);
+ mBroadcastSent = intent;
+ }
+
+ // Methods below are for managing the fake's behavior.
+
+ public void pokeSystemClockUpdateThreshold(int thresholdMillis) {
+ mSystemClockUpdateThresholdMillis = thresholdMillis;
+ }
+
+ public void pokeElapsedRealtimeMillis(long elapsedRealtimeMillis) {
+ mElapsedRealtimeMillis = elapsedRealtimeMillis;
+ }
+
+ public void pokeSystemClockMillis(long systemClockMillis) {
+ mSystemClockMillis = systemClockMillis;
+ }
+
+ public void pokeTimeDetectionEnabled(boolean enabled) {
+ mTimeDetectionEnabled = enabled;
+ }
+
+ public long peekElapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ public long peekSystemClockMillis() {
+ return mSystemClockMillis;
+ }
+
+ public void simulateTimePassing(int incrementMillis) {
+ mElapsedRealtimeMillis += incrementMillis;
+ mSystemClockMillis += incrementMillis;
+ }
+
+ public void verifySystemClockNotSet() {
+ assertFalse(mSystemClockWasSet);
+ }
+
+ public void verifySystemClockWasSet(long expectSystemClockMillis) {
+ assertTrue(mSystemClockWasSet);
+ assertEquals(expectSystemClockMillis, mSystemClockMillis);
+ }
+
+ public void verifyIntentWasBroadcast() {
+ assertTrue(mBroadcastSent != null);
+ }
+
+ public void verifyIntentWasNotBroadcast() {
+ assertNull(mBroadcastSent);
+ }
+
+ public void resetCallTracking() {
+ mSystemClockWasSet = false;
+ mBroadcastSent = null;
+ }
+
+ private void assertWakeLockAcquired() {
+ assertTrue("The operation must be performed only after acquiring the wakelock",
+ mWakeLockAcquired);
+ }
+ }
+
+ /**
+ * A fluent helper class for tests.
+ */
+ private class Script {
+
+ private final FakeCallback mFakeCallback;
+ private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy;
+
+ public Script() {
+ mFakeCallback = new FakeCallback();
+ mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy();
+ mSimpleTimeDetectorStrategy.initialize(mFakeCallback);
+
+ }
+
+ Script pokeTimeDetectionEnabled(boolean enabled) {
+ mFakeCallback.pokeTimeDetectionEnabled(enabled);
+ return this;
+ }
+
+ Script pokeFakeClocks(Scenario scenario) {
+ mFakeCallback.pokeElapsedRealtimeMillis(scenario.getInitialRealTimeMillis());
+ mFakeCallback.pokeSystemClockMillis(scenario.getInitialSystemClockMillis());
+ return this;
+ }
+
+ Script pokeThresholds(int systemClockUpdateThreshold) {
+ mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
+ return this;
+ }
+
+ long peekElapsedRealtimeMillis() {
+ return mFakeCallback.peekElapsedRealtimeMillis();
+ }
+
+ long peekSystemClockMillis() {
+ return mFakeCallback.peekSystemClockMillis();
+ }
+
+ Script simulateTimeSignalReceived(TimeSignal timeSignal) {
+ mSimpleTimeDetectorStrategy.suggestTime(timeSignal);
+ return this;
+ }
+
+ Script simulateAutoTimeDetectionToggle() {
+ boolean enabled = !mFakeCallback.isTimeDetectionEnabled();
+ mFakeCallback.pokeTimeDetectionEnabled(enabled);
+ mSimpleTimeDetectorStrategy.handleAutoTimeDetectionToggle(enabled);
+ return this;
+ }
+
+ Script simulateTimePassing(int clockIncrement) {
+ mFakeCallback.simulateTimePassing(clockIncrement);
+ return this;
+ }
+
+ Script verifySystemClockWasNotSetAndResetCallTracking() {
+ mFakeCallback.verifySystemClockNotSet();
+ mFakeCallback.verifyIntentWasNotBroadcast();
+ mFakeCallback.resetCallTracking();
+ return this;
+ }
+
+ Script verifySystemClockWasSetAndResetCallTracking(long expectSystemClockMillis) {
+ mFakeCallback.verifySystemClockWasSet(expectSystemClockMillis);
+ mFakeCallback.verifyIntentWasBroadcast();
+ mFakeCallback.resetCallTracking();
+ return this;
+ }
+ }
+
+ /**
+ * A starting scenario used during tests. Describes a fictional "physical" reality.
+ */
+ private static class Scenario {
+
+ private final long mInitialDeviceSystemClockMillis;
+ private final long mInitialDeviceRealtimeMillis;
+ private final long mActualTimeMillis;
+
+ Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis) {
+ mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
+ mActualTimeMillis = timeMillis;
+ mInitialDeviceRealtimeMillis = elapsedRealtime;
+ }
+
+ long getInitialRealTimeMillis() {
+ return mInitialDeviceRealtimeMillis;
+ }
+
+ long getInitialSystemClockMillis() {
+ return mInitialDeviceSystemClockMillis;
+ }
+
+ long getActualTimeMillis() {
+ return mActualTimeMillis;
+ }
+
+ TimeSignal createTimeSignalForActual(String sourceId) {
+ TimestampedValue<Long> time = new TimestampedValue<>(
+ mInitialDeviceRealtimeMillis, mActualTimeMillis);
+ return new TimeSignal(sourceId, time);
+ }
+
+ static class Builder {
+
+ private long mInitialDeviceSystemClockMillis;
+ private long mInitialDeviceRealtimeMillis;
+ private long mActualTimeMillis;
+
+ Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
+ int hourOfDay, int minute, int second) {
+ mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
+ minute, second);
+ return this;
+ }
+
+ Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
+ mInitialDeviceRealtimeMillis = realtimeMillis;
+ return this;
+ }
+
+ Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
+ int minute, int second) {
+ mActualTimeMillis =
+ createUtcTime(year, monthInYear, day, hourOfDay, minute, second);
+ return this;
+ }
+
+ Scenario build() {
+ return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
+ mActualTimeMillis);
+ }
+ }
+ }
+
+ private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+ int second) {
+ Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+ cal.clear();
+ cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+ return cal.getTimeInMillis();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 22dea92..ed74cd7 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -16,6 +16,9 @@
package com.android.server.timedetector;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -23,36 +26,40 @@
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 static org.mockito.Mockito.when;
import android.app.timedetector.TimeSignal;
import android.content.Context;
+import android.content.pm.PackageManager;
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;
+import com.android.server.timedetector.TimeDetectorStrategy.Callback;
+
+import java.io.PrintWriter;
+
@RunWith(AndroidJUnit4.class)
public class TimeDetectorServiceTest {
- private TimeDetectorService mTimeDetectorService;
-
private Context mMockContext;
- private TimeDetectorStrategy mMockTimeDetectorStrategy;
+ private StubbedTimeDetectorStrategy mStubbedTimeDetectorStrategy;
+ private Callback mMockCallback;
+
+ private TimeDetectorService mTimeDetectorService;
@Before
public void setUp() {
mMockContext = mock(Context.class);
- mMockTimeDetectorStrategy = mock(TimeDetectorStrategy.class);
- mTimeDetectorService = new TimeDetectorService(mMockContext, mMockTimeDetectorStrategy);
- }
+ mMockCallback = mock(Callback.class);
+ mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy();
- @After
- public void tearDown() {
- verifyNoMoreInteractions(mMockContext, mMockTimeDetectorStrategy);
+ mTimeDetectorService = new TimeDetectorService(
+ mMockContext, mMockCallback,
+ mStubbedTimeDetectorStrategy);
}
@Test(expected=SecurityException.class)
@@ -78,11 +85,86 @@
verify(mMockContext)
.enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
- verify(mMockTimeDetectorStrategy).suggestTime(timeSignal);
+ mStubbedTimeDetectorStrategy.verifySuggestTimeCalled(timeSignal);
+ }
+
+ @Test
+ public void testDump() {
+ when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ mTimeDetectorService.dump(null, null, null);
+
+ verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
+ mStubbedTimeDetectorStrategy.verifyDumpCalled();
+ }
+
+ @Test
+ public void testAutoTimeDetectionToggle() {
+ when(mMockCallback.isTimeDetectionEnabled()).thenReturn(true);
+
+ mTimeDetectorService.handleAutoTimeDetectionToggle();
+
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(true);
+
+ when(mMockCallback.isTimeDetectionEnabled()).thenReturn(false);
+
+ mTimeDetectorService.handleAutoTimeDetectionToggle();
+
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
}
private static TimeSignal createNitzTimeSignal() {
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
return new TimeSignal(TimeSignal.SOURCE_ID_NITZ, timeValue);
}
+
+ private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
+
+ // Call tracking.
+ private TimeSignal mLastSuggestedTime;
+ private Boolean mLastAutoTimeDetectionToggle;
+ private boolean mDumpCalled;
+
+ @Override
+ public void initialize(Callback ignored) {
+ }
+
+ @Override
+ public void suggestTime(TimeSignal timeSignal) {
+ resetCallTracking();
+ mLastSuggestedTime = timeSignal;
+ }
+
+ @Override
+ public void handleAutoTimeDetectionToggle(boolean enabled) {
+ resetCallTracking();
+ mLastAutoTimeDetectionToggle = enabled;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
+ resetCallTracking();
+ mDumpCalled = true;
+ }
+
+ void resetCallTracking() {
+ mLastSuggestedTime = null;
+ mLastAutoTimeDetectionToggle = null;
+ mDumpCalled = false;
+ }
+
+ void verifySuggestTimeCalled(TimeSignal expectedSignal) {
+ assertEquals(expectedSignal, mLastSuggestedTime);
+ }
+
+ void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) {
+ assertNotNull(mLastAutoTimeDetectionToggle);
+ assertEquals(expectedEnable, mLastAutoTimeDetectionToggle);
+ }
+
+ void verifyDumpCalled() {
+ assertTrue(mDumpCalled);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
new file mode 100644
index 0000000..19d31cf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.timezonedetector;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for the {@link TimeZoneDetectorService}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneDetectorServiceTest {
+
+ private TimeZoneDetectorService mTimeZoneDetectorService;
+
+ @Before
+ public void setUp() {
+ final Context context = InstrumentationRegistry.getContext();
+ mTimeZoneDetectorService = new TimeZoneDetectorService(context);
+ }
+
+ @Test
+ public void testStubbedCall() {
+ mTimeZoneDetectorService.stubbedCall();
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index e82c115..d03c7e1 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -45,6 +45,7 @@
private int mCallId;
// Number
private Uri mAddress;
+ private Uri mLocalAddress;
private boolean mIsPullable;
// CALL_STATE_CONFIRMED / CALL_STATE_TERMINATED
private int mCallState;
@@ -69,6 +70,19 @@
}
/** @hide */
+ public ImsExternalCallState(int callId, Uri address, Uri localAddress,
+ boolean isPullable, int callState, int callType, boolean isCallheld) {
+ mCallId = callId;
+ mAddress = address;
+ mLocalAddress = localAddress;
+ mIsPullable = isPullable;
+ mCallState = callState;
+ mCallType = callType;
+ mIsHeld = isCallheld;
+ Rlog.d(TAG, "ImsExternalCallState = " + this);
+ }
+
+ /** @hide */
public ImsExternalCallState(Parcel in) {
mCallId = in.readInt();
ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 4334d3a..3138180 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -213,6 +213,17 @@
/**
* Notify the framework that the device is disconnected from the IMS network.
+ * <p>
+ * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any
+ * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent
+ * to the framework. For example,
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+ * and
+ * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+ * may be set to unavailable to ensure the framework knows these services are no longer
+ * available due to de-registration. If you do not report capability changes impacted by
+ * de-registration, the framework will not know which features are no longer available as a
+ * result.
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
diff --git a/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java b/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java
new file mode 100644
index 0000000..cc1d105
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java
@@ -0,0 +1,199 @@
+/* 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.internal.telephony;
+
+import android.app.PendingIntent;
+import android.net.Uri;
+import java.lang.UnsupportedOperationException;
+import java.util.List;
+
+public class ISmsBaseImpl extends ISms.Stub {
+
+ @Override
+ public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPkg) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPkg,
+ int messageIndex, int newStatus, byte[] pdu) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean copyMessageToIccEfForSubscriber(int subId, String callingPkg, int status,
+ byte[] pdu, byte[] smsc) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendDataForSubscriber(int subId, String callingPkg, String destAddr,
+ String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPkg,
+ String destAddr, String scAddr, int destPort, byte[] data,
+ PendingIntent sentIntent, PendingIntent deliveryIntent)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendTextForSubscriber(int subId, String callingPkg, String destAddr,
+ String scAddr, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPkg,
+ String destAddr, String scAddr, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent, boolean persistMessage)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendTextForSubscriberWithOptions(int subId, String callingPkg, String destAddr,
+ String scAddr, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp,
+ int priority, boolean expectMore, int validityPeriod)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void injectSmsPduForSubscriber(
+ int subId, byte[] pdu, String format, PendingIntent receivedIntent)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendMultipartTextForSubscriber(int subId, String callingPkg,
+ String destinationAddress, String scAddress,
+ List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendMultipartTextForSubscriberWithOptions(int subId, String callingPkg,
+ String destinationAddress, String scAddress,
+ List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
+ int priority, boolean expectMore, int validityPeriod)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
+ int endMessageId, int ranType) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
+ int endMessageId, int ranType) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPremiumSmsPermission(String packageName) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPremiumSmsPermissionForSubscriber(int subId, String packageName)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setPremiumSmsPermission(String packageName, int permission) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setPremiumSmsPermissionForSubscriber(int subId, String packageName,
+ int permission) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isImsSmsSupportedForSubscriber(int subId) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isSmsSimPickActivityNeeded(int subId) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPreferredSmsSubscription() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getImsSmsFormatForSubscriber(int subId) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isSMSPromptEnabled() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
+ PendingIntent sentIntent, PendingIntent deliveryIntent)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
+ String scAddress, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+}