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