2 * Copyright (C) 2012 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.server;
19 import java.io.FileDescriptor;
20 import java.io.PrintWriter;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.PackageManager;
27 import android.net.ConnectivityManager;
28 import android.net.INetworkManagementEventObserver;
29 import android.net.InterfaceConfiguration;
30 import android.os.Binder;
31 import android.os.CommonTimeConfig;
32 import android.os.Handler;
33 import android.os.IBinder;
34 import android.os.INetworkManagementService;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.os.SystemProperties;
38 import android.util.Log;
40 import com.android.server.net.BaseNetworkObserver;
44 * <p>CommonTimeManagementService manages the configuration of the native Common Time service,
45 * reconfiguring the native service as appropriate in response to changes in network configuration.
47 class CommonTimeManagementService extends Binder {
49 * Constants and globals.
51 private static final String TAG = CommonTimeManagementService.class.getSimpleName();
52 private static final int NATIVE_SERVICE_RECONNECT_TIMEOUT = 5000;
53 private static final String AUTO_DISABLE_PROP = "ro.common_time.auto_disable";
54 private static final String ALLOW_WIFI_PROP = "ro.common_time.allow_wifi";
55 private static final String SERVER_PRIO_PROP = "ro.common_time.server_prio";
56 private static final String NO_INTERFACE_TIMEOUT_PROP = "ro.common_time.no_iface_timeout";
57 private static final boolean AUTO_DISABLE;
58 private static final boolean ALLOW_WIFI;
59 private static final byte BASE_SERVER_PRIO;
60 private static final int NO_INTERFACE_TIMEOUT;
61 private static final InterfaceScoreRule[] IFACE_SCORE_RULES;
65 AUTO_DISABLE = (0 != SystemProperties.getInt(AUTO_DISABLE_PROP, 1));
66 ALLOW_WIFI = (0 != SystemProperties.getInt(ALLOW_WIFI_PROP, 0));
67 tmp = SystemProperties.getInt(SERVER_PRIO_PROP, 1);
68 NO_INTERFACE_TIMEOUT = SystemProperties.getInt(NO_INTERFACE_TIMEOUT_PROP, 60000);
74 BASE_SERVER_PRIO = 30;
76 BASE_SERVER_PRIO = (byte)tmp;
79 IFACE_SCORE_RULES = new InterfaceScoreRule[] {
80 new InterfaceScoreRule("wlan", (byte)1),
81 new InterfaceScoreRule("eth", (byte)2),
84 IFACE_SCORE_RULES = new InterfaceScoreRule[] {
85 new InterfaceScoreRule("eth", (byte)2),
93 private final Context mContext;
94 private INetworkManagementService mNetMgr;
95 private CommonTimeConfig mCTConfig;
96 private String mCurIface;
97 private Handler mReconnectHandler = new Handler();
98 private Handler mNoInterfaceHandler = new Handler();
99 private Object mLock = new Object();
100 private boolean mDetectedAtStartup = false;
101 private byte mEffectivePrio = BASE_SERVER_PRIO;
104 * Callback handler implementations.
106 private INetworkManagementEventObserver mIfaceObserver = new BaseNetworkObserver() {
107 public void interfaceStatusChanged(String iface, boolean up) {
108 reevaluateServiceState();
110 public void interfaceLinkStateChanged(String iface, boolean up) {
111 reevaluateServiceState();
113 public void interfaceAdded(String iface) {
114 reevaluateServiceState();
116 public void interfaceRemoved(String iface) {
117 reevaluateServiceState();
121 private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
123 public void onReceive(Context context, Intent intent) {
124 reevaluateServiceState();
128 private CommonTimeConfig.OnServerDiedListener mCTServerDiedListener =
129 new CommonTimeConfig.OnServerDiedListener() {
130 public void onServerDied() {
131 scheduleTimeConfigReconnect();
135 private Runnable mReconnectRunnable = new Runnable() {
136 public void run() { connectToTimeConfig(); }
139 private Runnable mNoInterfaceRunnable = new Runnable() {
140 public void run() { handleNoInterfaceTimeout(); }
144 * Public interface (constructor, systemReady and dump)
146 public CommonTimeManagementService(Context context) {
150 void systemRunning() {
151 if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
152 Log.i(TAG, "No common time service detected on this platform. " +
153 "Common time services will be unavailable.");
157 mDetectedAtStartup = true;
159 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
160 mNetMgr = INetworkManagementService.Stub.asInterface(b);
162 // Network manager is running along-side us, so we should never receiver a remote exception
163 // while trying to register this observer.
165 mNetMgr.registerObserver(mIfaceObserver);
167 catch (RemoteException e) { }
169 // Register with the connectivity manager for connectivity changed intents.
170 IntentFilter filter = new IntentFilter();
171 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
172 mContext.registerReceiver(mConnectivityMangerObserver, filter);
174 // Connect to the common time config service and apply the initial configuration.
175 connectToTimeConfig();
179 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
180 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
181 != PackageManager.PERMISSION_GRANTED) {
182 pw.println(String.format(
183 "Permission Denial: can't dump CommonTimeManagement service from from " +
184 "pid=%d, uid=%d", Binder.getCallingPid(), Binder.getCallingUid()));
188 if (!mDetectedAtStartup) {
189 pw.println("Native Common Time service was not detected at startup. " +
190 "Service is unavailable");
194 synchronized (mLock) {
195 pw.println("Current Common Time Management Service Config:");
196 pw.println(String.format(" Native service : %s",
197 (null == mCTConfig) ? "reconnecting"
199 pw.println(String.format(" Bound interface : %s",
200 (null == mCurIface ? "unbound" : mCurIface)));
201 pw.println(String.format(" Allow WiFi : %s", ALLOW_WIFI ? "yes" : "no"));
202 pw.println(String.format(" Allow Auto Disable : %s", AUTO_DISABLE ? "yes" : "no"));
203 pw.println(String.format(" Server Priority : %d", mEffectivePrio));
204 pw.println(String.format(" No iface timeout : %d", NO_INTERFACE_TIMEOUT));
209 * Inner helper classes
211 private static class InterfaceScoreRule {
212 public final String mPrefix;
213 public final byte mScore;
214 public InterfaceScoreRule(String prefix, byte score) {
221 * Internal implementation
223 private void cleanupTimeConfig() {
224 mReconnectHandler.removeCallbacks(mReconnectRunnable);
225 mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
226 if (null != mCTConfig) {
232 private void connectToTimeConfig() {
233 // Get access to the common time service configuration interface. If we catch a remote
234 // exception in the process (service crashed or no running for w/e reason), schedule an
235 // attempt to reconnect in the future.
238 synchronized (mLock) {
239 mCTConfig = new CommonTimeConfig();
240 mCTConfig.setServerDiedListener(mCTServerDiedListener);
241 mCurIface = mCTConfig.getInterfaceBinding();
242 mCTConfig.setAutoDisable(AUTO_DISABLE);
243 mCTConfig.setMasterElectionPriority(mEffectivePrio);
246 if (NO_INTERFACE_TIMEOUT >= 0)
247 mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
249 reevaluateServiceState();
251 catch (RemoteException e) {
252 scheduleTimeConfigReconnect();
256 private void scheduleTimeConfigReconnect() {
258 Log.w(TAG, String.format("Native service died, will reconnect in %d mSec",
259 NATIVE_SERVICE_RECONNECT_TIMEOUT));
260 mReconnectHandler.postDelayed(mReconnectRunnable,
261 NATIVE_SERVICE_RECONNECT_TIMEOUT);
264 private void handleNoInterfaceTimeout() {
265 if (null != mCTConfig) {
266 Log.i(TAG, "Timeout waiting for interface to come up. " +
267 "Forcing networkless master mode.");
268 if (CommonTimeConfig.ERROR_DEAD_OBJECT == mCTConfig.forceNetworklessMasterMode())
269 scheduleTimeConfigReconnect();
273 private void reevaluateServiceState() {
274 String bindIface = null;
277 // Check to see if this interface is suitable to use for time synchronization.
279 // TODO : This selection algorithm needs to be enhanced for use with mobile devices. In
280 // particular, the choice of whether to a wireless interface or not should not be an all
281 // or nothing thing controlled by properties. It would probably be better if the
282 // platform had some concept of public wireless networks vs. home or friendly wireless
283 // networks (something a user would configure in settings or when a new interface is
284 // added). Then this algorithm could pick only wireless interfaces which were flagged
285 // as friendly, and be dormant when on public wireless networks.
287 // Another issue which needs to be dealt with is the use of driver supplied interface
288 // name to determine the network type. The fact that the wireless interface on a device
289 // is named "wlan0" is just a matter of convention; its not a 100% rule. For example,
290 // there are devices out there where the wireless is name "tiwlan0", not "wlan0". The
291 // internal network management interfaces in Android have all of the information needed
292 // to make a proper classification, there is just no way (currently) to fetch an
293 // interface's type (available from the ConnectionManager) as well as its address
294 // (available from either the java.net interfaces or from the NetworkManagment service).
295 // Both can enumerate interfaces, but that is no way to correlate their results (no
296 // common shared key; although using the interface name in the connection manager would
297 // be a good start). Until this gets resolved, we resort to substring searching for
298 // tags like wlan and eth.
300 String ifaceList[] = mNetMgr.listInterfaces();
301 if (null != ifaceList) {
302 for (String iface : ifaceList) {
305 for (InterfaceScoreRule r : IFACE_SCORE_RULES) {
306 if (iface.contains(r.mPrefix)) {
307 thisScore = r.mScore;
312 if (thisScore <= bestScore)
315 InterfaceConfiguration config = mNetMgr.getInterfaceConfig(iface);
319 if (config.isActive()) {
321 bestScore = thisScore;
326 catch (RemoteException e) {
327 // Bad news; we should not be getting remote exceptions from the connectivity manager
328 // since it is running in SystemServer along side of us. It probably does not matter
329 // what we do here, but go ahead and unbind the common time service in this case, just
330 // so we have some defined behavior.
334 boolean doRebind = true;
335 synchronized (mLock) {
336 if ((null != bindIface) && (null == mCurIface)) {
337 Log.e(TAG, String.format("Binding common time service to %s.", bindIface));
338 mCurIface = bindIface;
340 if ((null == bindIface) && (null != mCurIface)) {
341 Log.e(TAG, "Unbinding common time service.");
344 if ((null != bindIface) && (null != mCurIface) && !bindIface.equals(mCurIface)) {
345 Log.e(TAG, String.format("Switching common time service binding from %s to %s.",
346 mCurIface, bindIface));
347 mCurIface = bindIface;
353 if (doRebind && (null != mCTConfig)) {
354 byte newPrio = (bestScore > 0)
355 ? (byte)(bestScore * BASE_SERVER_PRIO)
357 if (newPrio != mEffectivePrio) {
358 mEffectivePrio = newPrio;
359 mCTConfig.setMasterElectionPriority(mEffectivePrio);
362 int res = mCTConfig.setNetworkBinding(mCurIface);
363 if (res != CommonTimeConfig.SUCCESS)
364 scheduleTimeConfigReconnect();
366 else if (NO_INTERFACE_TIMEOUT >= 0) {
367 mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
368 if (null == mCurIface)
369 mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);