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