1 /* 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 17 package com.android.server; 18 19 import java.io.FileDescriptor; 20 import java.io.PrintWriter; 21 import java.net.InetAddress; 22 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.PackageManager; 28 import android.net.ConnectivityManager; 29 import android.net.IConnectivityManager; 30 import android.net.INetworkManagementEventObserver; 31 import android.net.InterfaceConfiguration; 32 import android.net.NetworkInfo; 33 import android.os.Binder; 34 import android.os.CommonTimeConfig; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.INetworkManagementService; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.os.SystemProperties; 41 import android.util.Log; 42 43 import com.android.server.net.BaseNetworkObserver; 44 45 /** 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 */ 50 class 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 */ 109 private INetworkManagementEventObserver mIfaceObserver = new BaseNetworkObserver() { 110 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 } 122 }; 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 153 void systemRunning() { 154 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 } 377