/services/java/com/android/server/CommonTimeManagementService.java
https://github.com/aizuzi/platform_frameworks_base · Java · 376 lines · 264 code · 40 blank · 72 comment · 54 complexity · 7607c5e672f9c7524a34154c0a6f42a3 MD5 · raw file
- /*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.android.server;
- import java.io.FileDescriptor;
- import java.io.PrintWriter;
- import java.net.InetAddress;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.content.pm.PackageManager;
- import android.net.ConnectivityManager;
- import android.net.IConnectivityManager;
- import android.net.INetworkManagementEventObserver;
- import android.net.InterfaceConfiguration;
- import android.net.NetworkInfo;
- import android.os.Binder;
- import android.os.CommonTimeConfig;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.INetworkManagementService;
- import android.os.RemoteException;
- import android.os.ServiceManager;
- import android.os.SystemProperties;
- import android.util.Log;
- import com.android.server.net.BaseNetworkObserver;
- /**
- * @hide
- * <p>CommonTimeManagementService manages the configuration of the native Common Time service,
- * reconfiguring the native service as appropriate in response to changes in network configuration.
- */
- class CommonTimeManagementService extends Binder {
- /*
- * Constants and globals.
- */
- private static final String TAG = CommonTimeManagementService.class.getSimpleName();
- private static final int NATIVE_SERVICE_RECONNECT_TIMEOUT = 5000;
- private static final String AUTO_DISABLE_PROP = "ro.common_time.auto_disable";
- private static final String ALLOW_WIFI_PROP = "ro.common_time.allow_wifi";
- private static final String SERVER_PRIO_PROP = "ro.common_time.server_prio";
- private static final String NO_INTERFACE_TIMEOUT_PROP = "ro.common_time.no_iface_timeout";
- private static final boolean AUTO_DISABLE;
- private static final boolean ALLOW_WIFI;
- private static final byte BASE_SERVER_PRIO;
- private static final int NO_INTERFACE_TIMEOUT;
- private static final InterfaceScoreRule[] IFACE_SCORE_RULES;
- static {
- int tmp;
- AUTO_DISABLE = (0 != SystemProperties.getInt(AUTO_DISABLE_PROP, 1));
- ALLOW_WIFI = (0 != SystemProperties.getInt(ALLOW_WIFI_PROP, 0));
- tmp = SystemProperties.getInt(SERVER_PRIO_PROP, 1);
- NO_INTERFACE_TIMEOUT = SystemProperties.getInt(NO_INTERFACE_TIMEOUT_PROP, 60000);
- if (tmp < 1)
- BASE_SERVER_PRIO = 1;
- else
- if (tmp > 30)
- BASE_SERVER_PRIO = 30;
- else
- BASE_SERVER_PRIO = (byte)tmp;
- if (ALLOW_WIFI) {
- IFACE_SCORE_RULES = new InterfaceScoreRule[] {
- new InterfaceScoreRule("wlan", (byte)1),
- new InterfaceScoreRule("eth", (byte)2),
- };
- } else {
- IFACE_SCORE_RULES = new InterfaceScoreRule[] {
- new InterfaceScoreRule("eth", (byte)2),
- };
- }
- };
- /*
- * Internal state
- */
- private final Context mContext;
- private INetworkManagementService mNetMgr;
- private CommonTimeConfig mCTConfig;
- private String mCurIface;
- private Handler mReconnectHandler = new Handler();
- private Handler mNoInterfaceHandler = new Handler();
- private Object mLock = new Object();
- private boolean mDetectedAtStartup = false;
- private byte mEffectivePrio = BASE_SERVER_PRIO;
- /*
- * Callback handler implementations.
- */
- private INetworkManagementEventObserver mIfaceObserver = new BaseNetworkObserver() {
- public void interfaceStatusChanged(String iface, boolean up) {
- reevaluateServiceState();
- }
- public void interfaceLinkStateChanged(String iface, boolean up) {
- reevaluateServiceState();
- }
- public void interfaceAdded(String iface) {
- reevaluateServiceState();
- }
- public void interfaceRemoved(String iface) {
- reevaluateServiceState();
- }
- };
- private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- reevaluateServiceState();
- }
- };
- private CommonTimeConfig.OnServerDiedListener mCTServerDiedListener =
- new CommonTimeConfig.OnServerDiedListener() {
- public void onServerDied() {
- scheduleTimeConfigReconnect();
- }
- };
- private Runnable mReconnectRunnable = new Runnable() {
- public void run() { connectToTimeConfig(); }
- };
- private Runnable mNoInterfaceRunnable = new Runnable() {
- public void run() { handleNoInterfaceTimeout(); }
- };
- /*
- * Public interface (constructor, systemReady and dump)
- */
- public CommonTimeManagementService(Context context) {
- mContext = context;
- }
- void systemRunning() {
- if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
- Log.i(TAG, "No common time service detected on this platform. " +
- "Common time services will be unavailable.");
- return;
- }
- mDetectedAtStartup = true;
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- mNetMgr = INetworkManagementService.Stub.asInterface(b);
- // Network manager is running along-side us, so we should never receiver a remote exception
- // while trying to register this observer.
- try {
- mNetMgr.registerObserver(mIfaceObserver);
- }
- catch (RemoteException e) { }
- // Register with the connectivity manager for connectivity changed intents.
- IntentFilter filter = new IntentFilter();
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiver(mConnectivityMangerObserver, filter);
- // Connect to the common time config service and apply the initial configuration.
- connectToTimeConfig();
- }
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println(String.format(
- "Permission Denial: can't dump CommonTimeManagement service from from " +
- "pid=%d, uid=%d", Binder.getCallingPid(), Binder.getCallingUid()));
- return;
- }
- if (!mDetectedAtStartup) {
- pw.println("Native Common Time service was not detected at startup. " +
- "Service is unavailable");
- return;
- }
- synchronized (mLock) {
- pw.println("Current Common Time Management Service Config:");
- pw.println(String.format(" Native service : %s",
- (null == mCTConfig) ? "reconnecting"
- : "alive"));
- pw.println(String.format(" Bound interface : %s",
- (null == mCurIface ? "unbound" : mCurIface)));
- pw.println(String.format(" Allow WiFi : %s", ALLOW_WIFI ? "yes" : "no"));
- pw.println(String.format(" Allow Auto Disable : %s", AUTO_DISABLE ? "yes" : "no"));
- pw.println(String.format(" Server Priority : %d", mEffectivePrio));
- pw.println(String.format(" No iface timeout : %d", NO_INTERFACE_TIMEOUT));
- }
- }
- /*
- * Inner helper classes
- */
- private static class InterfaceScoreRule {
- public final String mPrefix;
- public final byte mScore;
- public InterfaceScoreRule(String prefix, byte score) {
- mPrefix = prefix;
- mScore = score;
- }
- };
- /*
- * Internal implementation
- */
- private void cleanupTimeConfig() {
- mReconnectHandler.removeCallbacks(mReconnectRunnable);
- mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
- if (null != mCTConfig) {
- mCTConfig.release();
- mCTConfig = null;
- }
- }
- private void connectToTimeConfig() {
- // Get access to the common time service configuration interface. If we catch a remote
- // exception in the process (service crashed or no running for w/e reason), schedule an
- // attempt to reconnect in the future.
- cleanupTimeConfig();
- try {
- synchronized (mLock) {
- mCTConfig = new CommonTimeConfig();
- mCTConfig.setServerDiedListener(mCTServerDiedListener);
- mCurIface = mCTConfig.getInterfaceBinding();
- mCTConfig.setAutoDisable(AUTO_DISABLE);
- mCTConfig.setMasterElectionPriority(mEffectivePrio);
- }
- if (NO_INTERFACE_TIMEOUT >= 0)
- mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
- reevaluateServiceState();
- }
- catch (RemoteException e) {
- scheduleTimeConfigReconnect();
- }
- }
- private void scheduleTimeConfigReconnect() {
- cleanupTimeConfig();
- Log.w(TAG, String.format("Native service died, will reconnect in %d mSec",
- NATIVE_SERVICE_RECONNECT_TIMEOUT));
- mReconnectHandler.postDelayed(mReconnectRunnable,
- NATIVE_SERVICE_RECONNECT_TIMEOUT);
- }
- private void handleNoInterfaceTimeout() {
- if (null != mCTConfig) {
- Log.i(TAG, "Timeout waiting for interface to come up. " +
- "Forcing networkless master mode.");
- if (CommonTimeConfig.ERROR_DEAD_OBJECT == mCTConfig.forceNetworklessMasterMode())
- scheduleTimeConfigReconnect();
- }
- }
- private void reevaluateServiceState() {
- String bindIface = null;
- byte bestScore = -1;
- try {
- // Check to see if this interface is suitable to use for time synchronization.
- //
- // TODO : This selection algorithm needs to be enhanced for use with mobile devices. In
- // particular, the choice of whether to a wireless interface or not should not be an all
- // or nothing thing controlled by properties. It would probably be better if the
- // platform had some concept of public wireless networks vs. home or friendly wireless
- // networks (something a user would configure in settings or when a new interface is
- // added). Then this algorithm could pick only wireless interfaces which were flagged
- // as friendly, and be dormant when on public wireless networks.
- //
- // Another issue which needs to be dealt with is the use of driver supplied interface
- // name to determine the network type. The fact that the wireless interface on a device
- // is named "wlan0" is just a matter of convention; its not a 100% rule. For example,
- // there are devices out there where the wireless is name "tiwlan0", not "wlan0". The
- // internal network management interfaces in Android have all of the information needed
- // to make a proper classification, there is just no way (currently) to fetch an
- // interface's type (available from the ConnectionManager) as well as its address
- // (available from either the java.net interfaces or from the NetworkManagment service).
- // Both can enumerate interfaces, but that is no way to correlate their results (no
- // common shared key; although using the interface name in the connection manager would
- // be a good start). Until this gets resolved, we resort to substring searching for
- // tags like wlan and eth.
- //
- String ifaceList[] = mNetMgr.listInterfaces();
- if (null != ifaceList) {
- for (String iface : ifaceList) {
- byte thisScore = -1;
- for (InterfaceScoreRule r : IFACE_SCORE_RULES) {
- if (iface.contains(r.mPrefix)) {
- thisScore = r.mScore;
- break;
- }
- }
- if (thisScore <= bestScore)
- continue;
- InterfaceConfiguration config = mNetMgr.getInterfaceConfig(iface);
- if (null == config)
- continue;
- if (config.isActive()) {
- bindIface = iface;
- bestScore = thisScore;
- }
- }
- }
- }
- catch (RemoteException e) {
- // Bad news; we should not be getting remote exceptions from the connectivity manager
- // since it is running in SystemServer along side of us. It probably does not matter
- // what we do here, but go ahead and unbind the common time service in this case, just
- // so we have some defined behavior.
- bindIface = null;
- }
- boolean doRebind = true;
- synchronized (mLock) {
- if ((null != bindIface) && (null == mCurIface)) {
- Log.e(TAG, String.format("Binding common time service to %s.", bindIface));
- mCurIface = bindIface;
- } else
- if ((null == bindIface) && (null != mCurIface)) {
- Log.e(TAG, "Unbinding common time service.");
- mCurIface = null;
- } else
- if ((null != bindIface) && (null != mCurIface) && !bindIface.equals(mCurIface)) {
- Log.e(TAG, String.format("Switching common time service binding from %s to %s.",
- mCurIface, bindIface));
- mCurIface = bindIface;
- } else {
- doRebind = false;
- }
- }
- if (doRebind && (null != mCTConfig)) {
- byte newPrio = (bestScore > 0)
- ? (byte)(bestScore * BASE_SERVER_PRIO)
- : BASE_SERVER_PRIO;
- if (newPrio != mEffectivePrio) {
- mEffectivePrio = newPrio;
- mCTConfig.setMasterElectionPriority(mEffectivePrio);
- }
- int res = mCTConfig.setNetworkBinding(mCurIface);
- if (res != CommonTimeConfig.SUCCESS)
- scheduleTimeConfigReconnect();
- else if (NO_INTERFACE_TIMEOUT >= 0) {
- mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
- if (null == mCurIface)
- mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
- }
- }
- }
- }