df86a9f2e9
Currently BaseNetworkObserver is in frameworks/base/services, but there is code in frameworks/base/core that could use it. This code typically extends INetworkManagementEventObserver.stub because BaseNetworkObserver is not available. Move BaseNetworkObserver to frameworks/base/core without changing its package name, and use it to simplify two callers. The third caller, Tethering, is much larger, and I'm not sure it's appropriate to change it. Bug: 10232006 Change-Id: Ifc0f2e619e3424e27e35730c048a1cc523df345e
377 lines
15 KiB
Java
377 lines
15 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|
|
}
|