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 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.pm.PackageManager; 27 import android.net.ConnectivityManager; 28 import android.net.INetworkManagementEventObserver; 29 import android.net.InterfaceConfiguration; 30 import android.os.Binder; 31 import android.os.CommonTimeConfig; 32 import android.os.Handler; 33 import android.os.IBinder; 34 import android.os.INetworkManagementService; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 37 import android.os.SystemProperties; 38 import android.util.Log; 39 40 import com.android.server.net.BaseNetworkObserver; 41 42 /** 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 */ 47 class 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 */ 106 private INetworkManagementEventObserver mIfaceObserver = new BaseNetworkObserver() { 107 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 } 119 }; 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 150 void systemRunning() { 151 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 } 374