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