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 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