blob: 5cebfa5a6edf2975ca42b0d83be1c7bb07fcf7b8 [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;
Andrew Scullb95ceab2017-05-19 11:04:38 +010095 private final Object mLock = new Object();
John Grossmanc1576732012-02-01 15:23:33 -080096 private INetworkManagementService mNetMgr;
97 private CommonTimeConfig mCTConfig;
98 private String mCurIface;
99 private Handler mReconnectHandler = new Handler();
100 private Handler mNoInterfaceHandler = new Handler();
John Grossmanc1576732012-02-01 15:23:33 -0800101 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() {
Andrew Scullb95ceab2017-05-19 11:04:38 +0100108 @Override
John Grossmanc1576732012-02-01 15:23:33 -0800109 public void interfaceStatusChanged(String iface, boolean up) {
110 reevaluateServiceState();
111 }
Andrew Scullb95ceab2017-05-19 11:04:38 +0100112 @Override
John Grossmanc1576732012-02-01 15:23:33 -0800113 public void interfaceLinkStateChanged(String iface, boolean up) {
114 reevaluateServiceState();
115 }
Andrew Scullb95ceab2017-05-19 11:04:38 +0100116 @Override
John Grossmanc1576732012-02-01 15:23:33 -0800117 public void interfaceAdded(String iface) {
118 reevaluateServiceState();
119 }
Andrew Scullb95ceab2017-05-19 11:04:38 +0100120 @Override
John Grossmanc1576732012-02-01 15:23:33 -0800121 public void interfaceRemoved(String iface) {
122 reevaluateServiceState();
123 }
John Grossmanc1576732012-02-01 15:23:33 -0800124 };
125
126 private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
127 @Override
128 public void onReceive(Context context, Intent intent) {
129 reevaluateServiceState();
130 }
131 };
132
133 private CommonTimeConfig.OnServerDiedListener mCTServerDiedListener =
Andrew Scullb95ceab2017-05-19 11:04:38 +0100134 () -> scheduleTimeConfigReconnect();
John Grossmanc1576732012-02-01 15:23:33 -0800135
Andrew Scullb95ceab2017-05-19 11:04:38 +0100136 private Runnable mReconnectRunnable = () -> connectToTimeConfig();
John Grossmanc1576732012-02-01 15:23:33 -0800137
Andrew Scullb95ceab2017-05-19 11:04:38 +0100138 private Runnable mNoInterfaceRunnable = () -> handleNoInterfaceTimeout();
John Grossmanc1576732012-02-01 15:23:33 -0800139
140 /*
141 * Public interface (constructor, systemReady and dump)
142 */
143 public CommonTimeManagementService(Context context) {
144 mContext = context;
145 }
146
Svetoslav Ganova0027152013-06-25 14:59:53 -0700147 void systemRunning() {
John Grossmanc1576732012-02-01 15:23:33 -0800148 if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
149 Log.i(TAG, "No common time service detected on this platform. " +
150 "Common time services will be unavailable.");
151 return;
152 }
153
154 mDetectedAtStartup = true;
155
156 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
157 mNetMgr = INetworkManagementService.Stub.asInterface(b);
158
159 // Network manager is running along-side us, so we should never receiver a remote exception
160 // while trying to register this observer.
161 try {
162 mNetMgr.registerObserver(mIfaceObserver);
163 }
164 catch (RemoteException e) { }
165
166 // Register with the connectivity manager for connectivity changed intents.
167 IntentFilter filter = new IntentFilter();
168 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
169 mContext.registerReceiver(mConnectivityMangerObserver, filter);
170
171 // Connect to the common time config service and apply the initial configuration.
172 connectToTimeConfig();
173 }
174
175 @Override
176 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600177 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
John Grossmanc1576732012-02-01 15:23:33 -0800178
179 if (!mDetectedAtStartup) {
180 pw.println("Native Common Time service was not detected at startup. " +
181 "Service is unavailable");
182 return;
183 }
184
185 synchronized (mLock) {
186 pw.println("Current Common Time Management Service Config:");
187 pw.println(String.format(" Native service : %s",
188 (null == mCTConfig) ? "reconnecting"
189 : "alive"));
190 pw.println(String.format(" Bound interface : %s",
191 (null == mCurIface ? "unbound" : mCurIface)));
192 pw.println(String.format(" Allow WiFi : %s", ALLOW_WIFI ? "yes" : "no"));
193 pw.println(String.format(" Allow Auto Disable : %s", AUTO_DISABLE ? "yes" : "no"));
194 pw.println(String.format(" Server Priority : %d", mEffectivePrio));
195 pw.println(String.format(" No iface timeout : %d", NO_INTERFACE_TIMEOUT));
196 }
197 }
198
199 /*
200 * Inner helper classes
201 */
202 private static class InterfaceScoreRule {
203 public final String mPrefix;
204 public final byte mScore;
205 public InterfaceScoreRule(String prefix, byte score) {
206 mPrefix = prefix;
207 mScore = score;
208 }
209 };
210
211 /*
212 * Internal implementation
213 */
214 private void cleanupTimeConfig() {
215 mReconnectHandler.removeCallbacks(mReconnectRunnable);
216 mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
217 if (null != mCTConfig) {
218 mCTConfig.release();
219 mCTConfig = null;
220 }
221 }
222
223 private void connectToTimeConfig() {
224 // Get access to the common time service configuration interface. If we catch a remote
225 // exception in the process (service crashed or no running for w/e reason), schedule an
226 // attempt to reconnect in the future.
227 cleanupTimeConfig();
228 try {
229 synchronized (mLock) {
230 mCTConfig = new CommonTimeConfig();
231 mCTConfig.setServerDiedListener(mCTServerDiedListener);
232 mCurIface = mCTConfig.getInterfaceBinding();
233 mCTConfig.setAutoDisable(AUTO_DISABLE);
234 mCTConfig.setMasterElectionPriority(mEffectivePrio);
235 }
236
237 if (NO_INTERFACE_TIMEOUT >= 0)
238 mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
239
240 reevaluateServiceState();
241 }
242 catch (RemoteException e) {
243 scheduleTimeConfigReconnect();
244 }
245 }
246
247 private void scheduleTimeConfigReconnect() {
248 cleanupTimeConfig();
249 Log.w(TAG, String.format("Native service died, will reconnect in %d mSec",
250 NATIVE_SERVICE_RECONNECT_TIMEOUT));
251 mReconnectHandler.postDelayed(mReconnectRunnable,
252 NATIVE_SERVICE_RECONNECT_TIMEOUT);
253 }
254
255 private void handleNoInterfaceTimeout() {
256 if (null != mCTConfig) {
257 Log.i(TAG, "Timeout waiting for interface to come up. " +
258 "Forcing networkless master mode.");
259 if (CommonTimeConfig.ERROR_DEAD_OBJECT == mCTConfig.forceNetworklessMasterMode())
260 scheduleTimeConfigReconnect();
261 }
262 }
263
264 private void reevaluateServiceState() {
265 String bindIface = null;
266 byte bestScore = -1;
267 try {
268 // Check to see if this interface is suitable to use for time synchronization.
269 //
270 // TODO : This selection algorithm needs to be enhanced for use with mobile devices. In
271 // particular, the choice of whether to a wireless interface or not should not be an all
272 // or nothing thing controlled by properties. It would probably be better if the
273 // platform had some concept of public wireless networks vs. home or friendly wireless
274 // networks (something a user would configure in settings or when a new interface is
275 // added). Then this algorithm could pick only wireless interfaces which were flagged
276 // as friendly, and be dormant when on public wireless networks.
277 //
278 // Another issue which needs to be dealt with is the use of driver supplied interface
279 // name to determine the network type. The fact that the wireless interface on a device
280 // is named "wlan0" is just a matter of convention; its not a 100% rule. For example,
281 // there are devices out there where the wireless is name "tiwlan0", not "wlan0". The
282 // internal network management interfaces in Android have all of the information needed
283 // to make a proper classification, there is just no way (currently) to fetch an
284 // interface's type (available from the ConnectionManager) as well as its address
285 // (available from either the java.net interfaces or from the NetworkManagment service).
286 // Both can enumerate interfaces, but that is no way to correlate their results (no
287 // common shared key; although using the interface name in the connection manager would
288 // be a good start). Until this gets resolved, we resort to substring searching for
289 // tags like wlan and eth.
290 //
291 String ifaceList[] = mNetMgr.listInterfaces();
292 if (null != ifaceList) {
293 for (String iface : ifaceList) {
294
295 byte thisScore = -1;
296 for (InterfaceScoreRule r : IFACE_SCORE_RULES) {
297 if (iface.contains(r.mPrefix)) {
298 thisScore = r.mScore;
299 break;
300 }
301 }
302
303 if (thisScore <= bestScore)
304 continue;
305
306 InterfaceConfiguration config = mNetMgr.getInterfaceConfig(iface);
307 if (null == config)
308 continue;
309
310 if (config.isActive()) {
311 bindIface = iface;
312 bestScore = thisScore;
313 }
314 }
315 }
316 }
317 catch (RemoteException e) {
318 // Bad news; we should not be getting remote exceptions from the connectivity manager
319 // since it is running in SystemServer along side of us. It probably does not matter
320 // what we do here, but go ahead and unbind the common time service in this case, just
321 // so we have some defined behavior.
322 bindIface = null;
323 }
324
325 boolean doRebind = true;
326 synchronized (mLock) {
327 if ((null != bindIface) && (null == mCurIface)) {
328 Log.e(TAG, String.format("Binding common time service to %s.", bindIface));
329 mCurIface = bindIface;
330 } else
331 if ((null == bindIface) && (null != mCurIface)) {
332 Log.e(TAG, "Unbinding common time service.");
333 mCurIface = null;
334 } else
335 if ((null != bindIface) && (null != mCurIface) && !bindIface.equals(mCurIface)) {
336 Log.e(TAG, String.format("Switching common time service binding from %s to %s.",
337 mCurIface, bindIface));
338 mCurIface = bindIface;
339 } else {
340 doRebind = false;
341 }
342 }
343
344 if (doRebind && (null != mCTConfig)) {
345 byte newPrio = (bestScore > 0)
346 ? (byte)(bestScore * BASE_SERVER_PRIO)
347 : BASE_SERVER_PRIO;
348 if (newPrio != mEffectivePrio) {
349 mEffectivePrio = newPrio;
350 mCTConfig.setMasterElectionPriority(mEffectivePrio);
351 }
352
353 int res = mCTConfig.setNetworkBinding(mCurIface);
354 if (res != CommonTimeConfig.SUCCESS)
355 scheduleTimeConfigReconnect();
356
357 else if (NO_INTERFACE_TIMEOUT >= 0) {
358 mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
359 if (null == mCurIface)
360 mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
361 }
362 }
363 }
364}