blob: 07c8679a6bf5557919a90518530d50a679387b35 [file] [log] [blame]
John Grossmanc1576732012-02-01 15:23:33 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package com.android.server;
18
19import java.io.FileDescriptor;
20import java.io.PrintWriter;
John Grossmanc1576732012-02-01 15:23:33 -080021
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
27import android.net.ConnectivityManager;
John Grossmanc1576732012-02-01 15:23:33 -080028import android.net.INetworkManagementEventObserver;
29import android.net.InterfaceConfiguration;
John Grossmanc1576732012-02-01 15:23:33 -080030import android.os.Binder;
31import android.os.CommonTimeConfig;
32import android.os.Handler;
33import android.os.IBinder;
34import android.os.INetworkManagementService;
35import android.os.RemoteException;
36import android.os.ServiceManager;
37import android.os.SystemProperties;
38import android.util.Log;
39
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060040import com.android.internal.util.DumpUtils;
Lorenzo Colittidf86a9f2013-08-20 19:51:30 +090041import com.android.server.net.BaseNetworkObserver;
42
John Grossmanc1576732012-02-01 15:23:33 -080043/**
44 * @hide
45 * <p>CommonTimeManagementService manages the configuration of the native Common Time service,
46 * reconfiguring the native service as appropriate in response to changes in network configuration.
47 */
48class CommonTimeManagementService extends Binder {
49 /*
50 * Constants and globals.
51 */
52 private static final String TAG = CommonTimeManagementService.class.getSimpleName();
53 private static final int NATIVE_SERVICE_RECONNECT_TIMEOUT = 5000;
54 private static final String AUTO_DISABLE_PROP = "ro.common_time.auto_disable";
55 private static final String ALLOW_WIFI_PROP = "ro.common_time.allow_wifi";
56 private static final String SERVER_PRIO_PROP = "ro.common_time.server_prio";
57 private static final String NO_INTERFACE_TIMEOUT_PROP = "ro.common_time.no_iface_timeout";
58 private static final boolean AUTO_DISABLE;
59 private static final boolean ALLOW_WIFI;
60 private static final byte BASE_SERVER_PRIO;
61 private static final int NO_INTERFACE_TIMEOUT;
62 private static final InterfaceScoreRule[] IFACE_SCORE_RULES;
63
64 static {
65 int tmp;
66 AUTO_DISABLE = (0 != SystemProperties.getInt(AUTO_DISABLE_PROP, 1));
67 ALLOW_WIFI = (0 != SystemProperties.getInt(ALLOW_WIFI_PROP, 0));
68 tmp = SystemProperties.getInt(SERVER_PRIO_PROP, 1);
69 NO_INTERFACE_TIMEOUT = SystemProperties.getInt(NO_INTERFACE_TIMEOUT_PROP, 60000);
70
71 if (tmp < 1)
72 BASE_SERVER_PRIO = 1;
73 else
74 if (tmp > 30)
75 BASE_SERVER_PRIO = 30;
76 else
77 BASE_SERVER_PRIO = (byte)tmp;
78
79 if (ALLOW_WIFI) {
80 IFACE_SCORE_RULES = new InterfaceScoreRule[] {
81 new InterfaceScoreRule("wlan", (byte)1),
82 new InterfaceScoreRule("eth", (byte)2),
83 };
84 } else {
85 IFACE_SCORE_RULES = new InterfaceScoreRule[] {
86 new InterfaceScoreRule("eth", (byte)2),
87 };
88 }
89 };
90
91 /*
92 * Internal state
93 */
94 private final Context mContext;
95 private INetworkManagementService mNetMgr;
96 private CommonTimeConfig mCTConfig;
97 private String mCurIface;
98 private Handler mReconnectHandler = new Handler();
99 private Handler mNoInterfaceHandler = new Handler();
100 private Object mLock = new Object();
101 private boolean mDetectedAtStartup = false;
102 private byte mEffectivePrio = BASE_SERVER_PRIO;
103
104 /*
105 * Callback handler implementations.
106 */
Lorenzo Colittidf86a9f2013-08-20 19:51:30 +0900107 private INetworkManagementEventObserver mIfaceObserver = new BaseNetworkObserver() {
John Grossmanc1576732012-02-01 15:23:33 -0800108 public void interfaceStatusChanged(String iface, boolean up) {
109 reevaluateServiceState();
110 }
111 public void interfaceLinkStateChanged(String iface, boolean up) {
112 reevaluateServiceState();
113 }
114 public void interfaceAdded(String iface) {
115 reevaluateServiceState();
116 }
117 public void interfaceRemoved(String iface) {
118 reevaluateServiceState();
119 }
John Grossmanc1576732012-02-01 15:23:33 -0800120 };
121
122 private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
123 @Override
124 public void onReceive(Context context, Intent intent) {
125 reevaluateServiceState();
126 }
127 };
128
129 private CommonTimeConfig.OnServerDiedListener mCTServerDiedListener =
130 new CommonTimeConfig.OnServerDiedListener() {
131 public void onServerDied() {
132 scheduleTimeConfigReconnect();
133 }
134 };
135
136 private Runnable mReconnectRunnable = new Runnable() {
137 public void run() { connectToTimeConfig(); }
138 };
139
140 private Runnable mNoInterfaceRunnable = new Runnable() {
141 public void run() { handleNoInterfaceTimeout(); }
142 };
143
144 /*
145 * Public interface (constructor, systemReady and dump)
146 */
147 public CommonTimeManagementService(Context context) {
148 mContext = context;
149 }
150
Svetoslav Ganova0027152013-06-25 14:59:53 -0700151 void systemRunning() {
John Grossmanc1576732012-02-01 15:23:33 -0800152 if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
153 Log.i(TAG, "No common time service detected on this platform. " +
154 "Common time services will be unavailable.");
155 return;
156 }
157
158 mDetectedAtStartup = true;
159
160 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
161 mNetMgr = INetworkManagementService.Stub.asInterface(b);
162
163 // Network manager is running along-side us, so we should never receiver a remote exception
164 // while trying to register this observer.
165 try {
166 mNetMgr.registerObserver(mIfaceObserver);
167 }
168 catch (RemoteException e) { }
169
170 // Register with the connectivity manager for connectivity changed intents.
171 IntentFilter filter = new IntentFilter();
172 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
173 mContext.registerReceiver(mConnectivityMangerObserver, filter);
174
175 // Connect to the common time config service and apply the initial configuration.
176 connectToTimeConfig();
177 }
178
179 @Override
180 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600181 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
John Grossmanc1576732012-02-01 15:23:33 -0800182
183 if (!mDetectedAtStartup) {
184 pw.println("Native Common Time service was not detected at startup. " +
185 "Service is unavailable");
186 return;
187 }
188
189 synchronized (mLock) {
190 pw.println("Current Common Time Management Service Config:");
191 pw.println(String.format(" Native service : %s",
192 (null == mCTConfig) ? "reconnecting"
193 : "alive"));
194 pw.println(String.format(" Bound interface : %s",
195 (null == mCurIface ? "unbound" : mCurIface)));
196 pw.println(String.format(" Allow WiFi : %s", ALLOW_WIFI ? "yes" : "no"));
197 pw.println(String.format(" Allow Auto Disable : %s", AUTO_DISABLE ? "yes" : "no"));
198 pw.println(String.format(" Server Priority : %d", mEffectivePrio));
199 pw.println(String.format(" No iface timeout : %d", NO_INTERFACE_TIMEOUT));
200 }
201 }
202
203 /*
204 * Inner helper classes
205 */
206 private static class InterfaceScoreRule {
207 public final String mPrefix;
208 public final byte mScore;
209 public InterfaceScoreRule(String prefix, byte score) {
210 mPrefix = prefix;
211 mScore = score;
212 }
213 };
214
215 /*
216 * Internal implementation
217 */
218 private void cleanupTimeConfig() {
219 mReconnectHandler.removeCallbacks(mReconnectRunnable);
220 mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
221 if (null != mCTConfig) {
222 mCTConfig.release();
223 mCTConfig = null;
224 }
225 }
226
227 private void connectToTimeConfig() {
228 // Get access to the common time service configuration interface. If we catch a remote
229 // exception in the process (service crashed or no running for w/e reason), schedule an
230 // attempt to reconnect in the future.
231 cleanupTimeConfig();
232 try {
233 synchronized (mLock) {
234 mCTConfig = new CommonTimeConfig();
235 mCTConfig.setServerDiedListener(mCTServerDiedListener);
236 mCurIface = mCTConfig.getInterfaceBinding();
237 mCTConfig.setAutoDisable(AUTO_DISABLE);
238 mCTConfig.setMasterElectionPriority(mEffectivePrio);
239 }
240
241 if (NO_INTERFACE_TIMEOUT >= 0)
242 mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
243
244 reevaluateServiceState();
245 }
246 catch (RemoteException e) {
247 scheduleTimeConfigReconnect();
248 }
249 }
250
251 private void scheduleTimeConfigReconnect() {
252 cleanupTimeConfig();
253 Log.w(TAG, String.format("Native service died, will reconnect in %d mSec",
254 NATIVE_SERVICE_RECONNECT_TIMEOUT));
255 mReconnectHandler.postDelayed(mReconnectRunnable,
256 NATIVE_SERVICE_RECONNECT_TIMEOUT);
257 }
258
259 private void handleNoInterfaceTimeout() {
260 if (null != mCTConfig) {
261 Log.i(TAG, "Timeout waiting for interface to come up. " +
262 "Forcing networkless master mode.");
263 if (CommonTimeConfig.ERROR_DEAD_OBJECT == mCTConfig.forceNetworklessMasterMode())
264 scheduleTimeConfigReconnect();
265 }
266 }
267
268 private void reevaluateServiceState() {
269 String bindIface = null;
270 byte bestScore = -1;
271 try {
272 // Check to see if this interface is suitable to use for time synchronization.
273 //
274 // TODO : This selection algorithm needs to be enhanced for use with mobile devices. In
275 // particular, the choice of whether to a wireless interface or not should not be an all
276 // or nothing thing controlled by properties. It would probably be better if the
277 // platform had some concept of public wireless networks vs. home or friendly wireless
278 // networks (something a user would configure in settings or when a new interface is
279 // added). Then this algorithm could pick only wireless interfaces which were flagged
280 // as friendly, and be dormant when on public wireless networks.
281 //
282 // Another issue which needs to be dealt with is the use of driver supplied interface
283 // name to determine the network type. The fact that the wireless interface on a device
284 // is named "wlan0" is just a matter of convention; its not a 100% rule. For example,
285 // there are devices out there where the wireless is name "tiwlan0", not "wlan0". The
286 // internal network management interfaces in Android have all of the information needed
287 // to make a proper classification, there is just no way (currently) to fetch an
288 // interface's type (available from the ConnectionManager) as well as its address
289 // (available from either the java.net interfaces or from the NetworkManagment service).
290 // Both can enumerate interfaces, but that is no way to correlate their results (no
291 // common shared key; although using the interface name in the connection manager would
292 // be a good start). Until this gets resolved, we resort to substring searching for
293 // tags like wlan and eth.
294 //
295 String ifaceList[] = mNetMgr.listInterfaces();
296 if (null != ifaceList) {
297 for (String iface : ifaceList) {
298
299 byte thisScore = -1;
300 for (InterfaceScoreRule r : IFACE_SCORE_RULES) {
301 if (iface.contains(r.mPrefix)) {
302 thisScore = r.mScore;
303 break;
304 }
305 }
306
307 if (thisScore <= bestScore)
308 continue;
309
310 InterfaceConfiguration config = mNetMgr.getInterfaceConfig(iface);
311 if (null == config)
312 continue;
313
314 if (config.isActive()) {
315 bindIface = iface;
316 bestScore = thisScore;
317 }
318 }
319 }
320 }
321 catch (RemoteException e) {
322 // Bad news; we should not be getting remote exceptions from the connectivity manager
323 // since it is running in SystemServer along side of us. It probably does not matter
324 // what we do here, but go ahead and unbind the common time service in this case, just
325 // so we have some defined behavior.
326 bindIface = null;
327 }
328
329 boolean doRebind = true;
330 synchronized (mLock) {
331 if ((null != bindIface) && (null == mCurIface)) {
332 Log.e(TAG, String.format("Binding common time service to %s.", bindIface));
333 mCurIface = bindIface;
334 } else
335 if ((null == bindIface) && (null != mCurIface)) {
336 Log.e(TAG, "Unbinding common time service.");
337 mCurIface = null;
338 } else
339 if ((null != bindIface) && (null != mCurIface) && !bindIface.equals(mCurIface)) {
340 Log.e(TAG, String.format("Switching common time service binding from %s to %s.",
341 mCurIface, bindIface));
342 mCurIface = bindIface;
343 } else {
344 doRebind = false;
345 }
346 }
347
348 if (doRebind && (null != mCTConfig)) {
349 byte newPrio = (bestScore > 0)
350 ? (byte)(bestScore * BASE_SERVER_PRIO)
351 : BASE_SERVER_PRIO;
352 if (newPrio != mEffectivePrio) {
353 mEffectivePrio = newPrio;
354 mCTConfig.setMasterElectionPriority(mEffectivePrio);
355 }
356
357 int res = mCTConfig.setNetworkBinding(mCurIface);
358 if (res != CommonTimeConfig.SUCCESS)
359 scheduleTimeConfigReconnect();
360
361 else if (NO_INTERFACE_TIMEOUT >= 0) {
362 mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
363 if (null == mCurIface)
364 mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
365 }
366 }
367 }
368}