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 final Object mLock = new Object();
     96     private INetworkManagementService mNetMgr;
     97     private CommonTimeConfig mCTConfig;
     98     private String mCurIface;
     99     private Handler mReconnectHandler = new Handler();
    100     private Handler mNoInterfaceHandler = new Handler();
    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         @Override
    109         public void interfaceStatusChanged(String iface, boolean up) {
    110             reevaluateServiceState();
    111         }
    112         @Override
    113         public void interfaceLinkStateChanged(String iface, boolean up) {
    114             reevaluateServiceState();
    115         }
    116         @Override
    117         public void interfaceAdded(String iface) {
    118             reevaluateServiceState();
    119         }
    120         @Override
    121         public void interfaceRemoved(String iface) {
    122             reevaluateServiceState();
    123         }
    124     };
    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 =
    134             () -> scheduleTimeConfigReconnect();
    135 
    136     private Runnable mReconnectRunnable = () -> connectToTimeConfig();
    137 
    138     private Runnable mNoInterfaceRunnable = () -> handleNoInterfaceTimeout();
    139 
    140     /*
    141      * Public interface (constructor, systemReady and dump)
    142      */
    143     public CommonTimeManagementService(Context context) {
    144         mContext = context;
    145     }
    146 
    147     void systemRunning() {
    148         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) {
    177         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
    178 
    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 }
    365