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