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