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