OSDN Git Service

Merge \\\\\\\\\"DO NOT MERGE revert public api loadSafeLabel\\\\\\\\\" into lmp-dev...
[android-x86/frameworks-base.git] / services / core / java / com / android / server / CommonTimeManagementService.java
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 }