• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
CommonTimeManagementService(Context context)152     public CommonTimeManagementService(Context context) {
153         mContext = context;
154     }
155 
systemReady()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
dump(FileDescriptor fd, PrintWriter pw, String[] args)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;
InterfaceScoreRule(String prefix, byte score)220         public InterfaceScoreRule(String prefix, byte score) {
221             mPrefix = prefix;
222             mScore = score;
223         }
224     };
225 
226     /*
227      * Internal implementation
228      */
cleanupTimeConfig()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 
connectToTimeConfig()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 
scheduleTimeConfigReconnect()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 
handleNoInterfaceTimeout()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 
reevaluateServiceState()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