blob: 60b366aca5bc22dae0c89cbf3a0e571ea0d5e3f7 [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
Lorenzo Colittidf86a9f2013-08-20 19:51:30 +090040import com.android.server.net.BaseNetworkObserver;
41
John Grossmanc1576732012-02-01 15:23:33 -080042/**
43 * @hide
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.
46 */
47class CommonTimeManagementService extends Binder {
48 /*
49 * Constants and globals.
50 */
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;
62
63 static {
64 int tmp;
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);
69
70 if (tmp < 1)
71 BASE_SERVER_PRIO = 1;
72 else
73 if (tmp > 30)
74 BASE_SERVER_PRIO = 30;
75 else
76 BASE_SERVER_PRIO = (byte)tmp;
77
78 if (ALLOW_WIFI) {
79 IFACE_SCORE_RULES = new InterfaceScoreRule[] {
80 new InterfaceScoreRule("wlan", (byte)1),
81 new InterfaceScoreRule("eth", (byte)2),
82 };
83 } else {
84 IFACE_SCORE_RULES = new InterfaceScoreRule[] {
85 new InterfaceScoreRule("eth", (byte)2),
86 };
87 }
88 };
89
90 /*
91 * Internal state
92 */
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;
102
103 /*
104 * Callback handler implementations.
105 */
Lorenzo Colittidf86a9f2013-08-20 19:51:30 +0900106 private INetworkManagementEventObserver mIfaceObserver = new BaseNetworkObserver() {
John Grossmanc1576732012-02-01 15:23:33 -0800107 public void interfaceStatusChanged(String iface, boolean up) {
108 reevaluateServiceState();
109 }
110 public void interfaceLinkStateChanged(String iface, boolean up) {
111 reevaluateServiceState();
112 }
113 public void interfaceAdded(String iface) {
114 reevaluateServiceState();
115 }
116 public void interfaceRemoved(String iface) {
117 reevaluateServiceState();
118 }
John Grossmanc1576732012-02-01 15:23:33 -0800119 };
120
121 private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
122 @Override
123 public void onReceive(Context context, Intent intent) {
124 reevaluateServiceState();
125 }
126 };
127
128 private CommonTimeConfig.OnServerDiedListener mCTServerDiedListener =
129 new CommonTimeConfig.OnServerDiedListener() {
130 public void onServerDied() {
131 scheduleTimeConfigReconnect();
132 }
133 };
134
135 private Runnable mReconnectRunnable = new Runnable() {
136 public void run() { connectToTimeConfig(); }
137 };
138
139 private Runnable mNoInterfaceRunnable = new Runnable() {
140 public void run() { handleNoInterfaceTimeout(); }
141 };
142
143 /*
144 * Public interface (constructor, systemReady and dump)
145 */
146 public CommonTimeManagementService(Context context) {
147 mContext = context;
148 }
149
Svetoslav Ganova0027152013-06-25 14:59:53 -0700150 void systemRunning() {
John Grossmanc1576732012-02-01 15:23:33 -0800151 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.");
154 return;
155 }
156
157 mDetectedAtStartup = true;
158
159 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
160 mNetMgr = INetworkManagementService.Stub.asInterface(b);
161
162 // Network manager is running along-side us, so we should never receiver a remote exception
163 // while trying to register this observer.
164 try {
165 mNetMgr.registerObserver(mIfaceObserver);
166 }
167 catch (RemoteException e) { }
168
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);
173
174 // Connect to the common time config service and apply the initial configuration.
175 connectToTimeConfig();
176 }
177
178 @Override
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()));
185 return;
186 }
187
188 if (!mDetectedAtStartup) {
189 pw.println("Native Common Time service was not detected at startup. " +
190 "Service is unavailable");
191 return;
192 }
193
194 synchronized (mLock) {
195 pw.println("Current Common Time Management Service Config:");
196 pw.println(String.format(" Native service : %s",
197 (null == mCTConfig) ? "reconnecting"
198 : "alive"));
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));
205 }
206 }
207
208 /*
209 * Inner helper classes
210 */
211 private static class InterfaceScoreRule {
212 public final String mPrefix;
213 public final byte mScore;
214 public InterfaceScoreRule(String prefix, byte score) {
215 mPrefix = prefix;
216 mScore = score;
217 }
218 };
219
220 /*
221 * Internal implementation
222 */
223 private void cleanupTimeConfig() {
224 mReconnectHandler.removeCallbacks(mReconnectRunnable);
225 mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
226 if (null != mCTConfig) {
227 mCTConfig.release();
228 mCTConfig = null;
229 }
230 }
231
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.
236 cleanupTimeConfig();
237 try {
238 synchronized (mLock) {
239 mCTConfig = new CommonTimeConfig();
240 mCTConfig.setServerDiedListener(mCTServerDiedListener);
241 mCurIface = mCTConfig.getInterfaceBinding();
242 mCTConfig.setAutoDisable(AUTO_DISABLE);
243 mCTConfig.setMasterElectionPriority(mEffectivePrio);
244 }
245
246 if (NO_INTERFACE_TIMEOUT >= 0)
247 mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
248
249 reevaluateServiceState();
250 }
251 catch (RemoteException e) {
252 scheduleTimeConfigReconnect();
253 }
254 }
255
256 private void scheduleTimeConfigReconnect() {
257 cleanupTimeConfig();
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);
262 }
263
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();
270 }
271 }
272
273 private void reevaluateServiceState() {
274 String bindIface = null;
275 byte bestScore = -1;
276 try {
277 // Check to see if this interface is suitable to use for time synchronization.
278 //
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.
286 //
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.
299 //
300 String ifaceList[] = mNetMgr.listInterfaces();
301 if (null != ifaceList) {
302 for (String iface : ifaceList) {
303
304 byte thisScore = -1;
305 for (InterfaceScoreRule r : IFACE_SCORE_RULES) {
306 if (iface.contains(r.mPrefix)) {
307 thisScore = r.mScore;
308 break;
309 }
310 }
311
312 if (thisScore <= bestScore)
313 continue;
314
315 InterfaceConfiguration config = mNetMgr.getInterfaceConfig(iface);
316 if (null == config)
317 continue;
318
319 if (config.isActive()) {
320 bindIface = iface;
321 bestScore = thisScore;
322 }
323 }
324 }
325 }
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.
331 bindIface = null;
332 }
333
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;
339 } else
340 if ((null == bindIface) && (null != mCurIface)) {
341 Log.e(TAG, "Unbinding common time service.");
342 mCurIface = null;
343 } else
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;
348 } else {
349 doRebind = false;
350 }
351 }
352
353 if (doRebind && (null != mCTConfig)) {
354 byte newPrio = (bestScore > 0)
355 ? (byte)(bestScore * BASE_SERVER_PRIO)
356 : BASE_SERVER_PRIO;
357 if (newPrio != mEffectivePrio) {
358 mEffectivePrio = newPrio;
359 mCTConfig.setMasterElectionPriority(mEffectivePrio);
360 }
361
362 int res = mCTConfig.setNetworkBinding(mCurIface);
363 if (res != CommonTimeConfig.SUCCESS)
364 scheduleTimeConfigReconnect();
365
366 else if (NO_INTERFACE_TIMEOUT >= 0) {
367 mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
368 if (null == mCurIface)
369 mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
370 }
371 }
372 }
373}