Android 9.0 网络之netd详解

这篇具有很好参考价值的文章主要介绍了Android 9.0 网络之netd详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、DHCP流程

  分析netd之前先了解一下网络自动获取IP流程,借鉴下图流程查看代码:

android netd,python,网络,数据库

 (1)WIFI扫描到可用网络后进行连接,代码路径:\frameworks\opt\net\wifi\service\java\com\android\server\wifi\WifiStateMachine.java

android netd,python,网络,数据库

                case WifiMonitor.NETWORK_CONNECTION_EVENT:
                    if (mVerboseLoggingEnabled) log("Network connection established");
                    mLastNetworkId = message.arg1;
                    mWifiConfigManager.clearRecentFailureReason(mLastNetworkId);
                    mLastBssid = (String) message.obj;
                    reasonCode = message.arg2;
                    // TODO: This check should not be needed after WifiStateMachinePrime refactor.
                    // Currently, the last connected network configuration is left in
                    // wpa_supplicant, this may result in wpa_supplicant initiating connection
                    // to it after a config store reload. Hence the old network Id lookups may not
                    // work, so disconnect the network and let network selector reselect a new
                    // network.
                    config = getCurrentWifiConfiguration();
                    if (config != null) {
                        mWifiInfo.setBSSID(mLastBssid);
                        mWifiInfo.setNetworkId(mLastNetworkId);
                        mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));

                        ScanDetailCache scanDetailCache =
                                mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
                        if (scanDetailCache != null && mLastBssid != null) {
                            ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid);
                            if (scanResult != null) {
                                mWifiInfo.setFrequency(scanResult.frequency);
                            }
                        }
                        mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
                        // We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
                        if (config.enterpriseConfig != null
                                && TelephonyUtil.isSimEapMethod(
                                        config.enterpriseConfig.getEapMethod())) {
                            String anonymousIdentity =
                                    mWifiNative.getEapAnonymousIdentity(mInterfaceName);
                            if (anonymousIdentity != null) {
                                config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
                            } else {
                                Log.d(TAG, "Failed to get updated anonymous identity"
                                        + " from supplicant, reset it in WifiConfiguration.");
                                config.enterpriseConfig.setAnonymousIdentity(null);
                            }
                            mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
                        }
                        sendNetworkStateChangeBroadcast(mLastBssid);
                        transitionTo(mObtainingIpState);
                    } else {
                        logw("Connected to unknown networkId " + mLastNetworkId
                                + ", disconnecting...");
                        sendMessage(CMD_DISCONNECT);
                    }
                    break;

android netd,python,网络,数据库

     其中 transitionTo(mObtainingIpState) 即调用如下,根据当前wifi配置文件信息,进行自动或静态IP配置:

android netd,python,网络,数据库

    class ObtainingIpState extends State {
        @Override
        public void enter() {
            final WifiConfiguration currentConfig = getCurrentWifiConfiguration();
            final boolean isUsingStaticIp =
                    (currentConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
            if (mVerboseLoggingEnabled) {
                final String key = currentConfig.configKey();
                log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
                        + " " + key + " "
                        + " roam=" + mIsAutoRoaming
                        + " static=" + isUsingStaticIp);
            }

            // Send event to CM & network change broadcast
            setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);

            // We must clear the config BSSID, as the wifi chipset may decide to roam
            // from this point on and having the BSSID specified in the network block would
            // cause the roam to fail and the device to disconnect.
            clearTargetBssid("ObtainingIpAddress");

            // Stop IpClient in case we're switching from DHCP to static
            // configuration or vice versa.
            //
            // TODO: Only ever enter this state the first time we connect to a
            // network, never on switching between static configuration and
            // DHCP. When we transition from static configuration to DHCP in
            // particular, we must tell ConnectivityService that we're
            // disconnected, because DHCP might take a long time during which
            // connectivity APIs such as getActiveNetworkInfo should not return
            // CONNECTED.
            stopIpClient();

            mIpClient.setHttpProxy(currentConfig.getHttpProxy());
            if (!TextUtils.isEmpty(mTcpBufferSizes)) {
                mIpClient.setTcpBufferSizes(mTcpBufferSizes);
            }
            final IpClient.ProvisioningConfiguration prov;
            if (!isUsingStaticIp) {
                prov = IpClient.buildProvisioningConfiguration()
                            .withPreDhcpAction()
                            .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                            .withNetwork(getCurrentNetwork())
                            .withDisplayName(currentConfig.SSID)
                            .withRandomMacAddress()
                            .build();
            } else {
                StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration();
                prov = IpClient.buildProvisioningConfiguration()
                            .withStaticConfiguration(staticIpConfig)
                            .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                            .withNetwork(getCurrentNetwork())
                            .withDisplayName(currentConfig.SSID)
                            .build();
            }
            mIpClient.startProvisioning(prov);
           // Get Link layer stats so as we get fresh tx packet counters
            getWifiLinkLayerStats();
        }

        @Override
        public boolean processMessage(Message message) {
            logStateAndMessage(message, this);

            switch(message.what) {
                case CMD_START_CONNECT:
                case CMD_START_ROAM:
                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
                    break;
                case WifiManager.SAVE_NETWORK:
                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
                    deferMessage(message);
                    break;
                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
                    reportConnectionAttemptEnd(
                            WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
                    return NOT_HANDLED;
                case CMD_SET_HIGH_PERF_MODE:
                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
                    deferMessage(message);
                    break;
                default:
                    return NOT_HANDLED;
            }
            return HANDLED;
        }
    }

android netd,python,网络,数据库

 将状态设为DetailedState.OBTAINING_IPADDR,初始化好配置后会调用IpClient的startProvisioning

android netd,python,网络,数据库

    public void startProvisioning(ProvisioningConfiguration req) {
        if (!req.isValid()) {
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
            return;
        }
 
        mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
        if (mInterfaceParams == null) {
            logError("Failed to find InterfaceParams for " + mInterfaceName);
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
            return;
        }
 
        mCallback.setNeighborDiscoveryOffload(true);
        sendMessage(CMD_START, new ProvisioningConfiguration(req));
    }

android netd,python,网络,数据库

IPClient的准备工作是随配置发出CMD_START的消息,进一步执行到RunningState 方法,enter方法区别是配置了ipv6还是ipv4走不同的流程,现在默认一般是ipv4。

android netd,python,网络,数据库

    class RunningState extends State {
        private ConnectivityPacketTracker mPacketTracker;
        private boolean mDhcpActionInFlight;

        @Override
        public void enter() {
            ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
            apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
            apfConfig.multicastFilter = mMulticastFiltering;
            // Get the Configuration for ApfFilter from Context
            apfConfig.ieee802_3Filter =
                    mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
            apfConfig.ethTypeBlackList =
                    mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
            mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
            // TODO: investigate the effects of any multicast filtering racing/interfering with the
            // rest of this IP configuration startup.
            if (mApfFilter == null) {
                mCallback.setFallbackMulticastFilter(mMulticastFiltering);
            }

            mPacketTracker = createPacketTracker();
            if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);

            if (mConfiguration.mEnableIPv6 && !startIPv6()) {
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
                transitionTo(mStoppingState);
                return;
            }

            if (mConfiguration.mEnableIPv4 && !startIPv4()) {
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
                transitionTo(mStoppingState);
                return;
            }

            final InitialConfiguration config = mConfiguration.mInitialConfig;
            if ((config != null) && !applyInitialConfig(config)) {
                // TODO introduce a new IpManagerEvent constant to distinguish this error case.
                doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
                transitionTo(mStoppingState);
                return;
            }

            if (mConfiguration.mUsingMultinetworkPolicyTracker) {
                mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(
                        mContext, getHandler(),
                        () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
                mMultinetworkPolicyTracker.start();
            }

            if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
                doImmediateProvisioningFailure(
                        IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
                transitionTo(mStoppingState);
                return;
            }
        }

android netd,python,网络,数据库

看下startIPv4逻辑:

android netd,python,网络,数据库

    private boolean startIPv4() {
        // If we have a StaticIpConfiguration attempt to apply it and
        // handle the result accordingly.
        if (mConfiguration.mStaticIpConfig != null) {
            if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
                handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
            } else {
                return false;
            }
        } else {
            // Start DHCPv4.
            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
            mDhcpClient.registerForPreDhcpNotification();
            mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
        }

        return true;
    }

android netd,python,网络,数据库

DhcpClient也处理CMD_START_DHCP开始状态变更,稍微等一下状态切换会回到mDhcpInitState开始。

android netd,python,网络,数据库

    class StoppedState extends State {
        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CMD_START_DHCP:
                    if (mRegisteredForPreDhcpNotification) {
                        transitionTo(mWaitBeforeStartState);
                    } else {
                        transitionTo(mDhcpInitState);
                    }
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
        }
    }
 
    private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
 
 
    // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
    // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
    abstract class WaitBeforeOtherState extends LoggingState {
        protected State mOtherState;
 
        @Override
        public void enter() {
            super.enter();
            mController.sendMessage(CMD_PRE_DHCP_ACTION);
        }
 
        @Override
        public boolean processMessage(Message message) {
            super.processMessage(message);
            switch (message.what) {
                case CMD_PRE_DHCP_ACTION_COMPLETE:
                    transitionTo(mOtherState);
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
        }
    }

android netd,python,网络,数据库

IPClient处理DhcpClient发来的CMD_CONFIGURE_LINKADDRESS

android netd,python,网络,数据库

                case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
                    final LinkAddress ipAddress = (LinkAddress) msg.obj;
                    if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
                        mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
                    } else {
                        logError("Failed to set IPv4 address.");
                        dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
                                new LinkProperties(mLinkProperties));
                        transitionTo(mStoppingState);
                    }
                    break;
                }

android netd,python,网络,数据库

android netd,python,网络,数据库

DhcpClient切换到mWaitBeforeStartState,这是由于之前有调用mDhcpClient.registerForPreDhcpNotification();所以这边状态等待其他状态完成,其实是dhcp有些准备工作需要在WifiStateMachine中完成,所以这边流程需要等一下,流程完了自然会切换到DhcpInitState。

android netd,python,网络,数据库

    class DhcpInitState extends PacketRetransmittingState {
        public DhcpInitState() {
            super();
        }
 
        @Override
        public void enter() {
            super.enter();
            startNewTransaction();
            mLastInitEnterTime = SystemClock.elapsedRealtime();
        }
 
        protected boolean sendPacket() {
            return sendDiscoverPacket();
        }
 
        protected void receivePacket(DhcpPacket packet) {
            if (!isValidPacket(packet)) return;
            if (!(packet instanceof DhcpOfferPacket)) return;
            mOffer = packet.toDhcpResults();
            if (mOffer != null) {
                Log.d(TAG, "Got pending lease: " + mOffer);
                transitionTo(mDhcpRequestingState);
            }
        }
    }

android netd,python,网络,数据库

DHCPREQUEST收到回应,向IpClient发出CMD_POST_DHCP_ACTION消息。

android netd,python,网络,数据库

    private void notifySuccess() {
        mController.sendMessage(
                CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
    }
 
 
    private void acceptDhcpResults(DhcpResults results, String msg) {
        mDhcpLease = results;
        mOffer = null;
        Log.d(TAG, msg + " lease: " + mDhcpLease);
        notifySuccess();
    }
 
    class DhcpRequestingState extends PacketRetransmittingState {
        public DhcpRequestingState() {
            mTimeout = DHCP_TIMEOUT_MS / 2;
        }
 
        protected boolean sendPacket() {
            return sendRequestPacket(
                    INADDR_ANY,                                    // ciaddr
                    (Inet4Address) mOffer.ipAddress.getAddress(),  // DHCP_REQUESTED_IP
                    (Inet4Address) mOffer.serverAddress,           // DHCP_SERVER_IDENTIFIER
                    INADDR_BROADCAST);                             // packet destination address
        }
 
        protected void receivePacket(DhcpPacket packet) {
            if (!isValidPacket(packet)) return;
            if ((packet instanceof DhcpAckPacket)) {
                DhcpResults results = packet.toDhcpResults();
                if (results != null) {
                    setDhcpLeaseExpiry(packet);
                    acceptDhcpResults(results, "Confirmed");
                    transitionTo(mConfiguringInterfaceState);
                }
            } else if (packet instanceof DhcpNakPacket) {
                // TODO: Wait a while before returning into INIT state.
                Log.d(TAG, "Received NAK, returning to INIT");
                mOffer = null;
                transitionTo(mDhcpInitState);
            }
        }

android netd,python,网络,数据库

IpClient进行处理:

android netd,python,网络,数据库

                case DhcpClient.CMD_POST_DHCP_ACTION:
                    stopDhcpAction();
 
                    switch (msg.arg1) {
                        case DhcpClient.DHCP_SUCCESS:
                            handleIPv4Success((DhcpResults) msg.obj);
                            break;
                        case DhcpClient.DHCP_FAILURE:
                            handleIPv4Failure();
                            break;
                        default:
                            logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
                    }
                    break;

android netd,python,网络,数据库

回调WifiStateMachine的callback:

        @Override
        public void onProvisioningSuccess(LinkProperties newLp) {
            mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
            sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
            sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
        }

ip配置成功后设置网络状态变为connected并发送广播通知,最后状态切到ConnectedState。

二、netd工作原理详解

  NETD是Android一个专门管理网络链接, 路由/带宽/防火墙策略以及iptables的系统Daemon进程, 其在Anroid系统启动时加载:

android netd,python,网络,数据库

service netd /system/bin/netd
    class main
    socket netd stream 0660 root system
    socket dnsproxyd stream 0660 root inet
    socket mdns stream 0660 root system
    socket fwmarkd stream 0660 root inet
    onrestart restart zygote
    onrestart restart zygote_secondary

android netd,python,网络,数据库

启动netd时, 会创建四个socket,用于其他进程与netd进行通信:

  • netd: 主要与Framework的NetworkManagementService交互, 用于控制网口状态, 路由表
  • dnsproxyd: DNS代理的控制与配置,用于私有DNS(DNS Over TLS)的请求转发
  • mdns: 多播DNS(Multicast DNS,参考RFCRFC 6762 - Multicast DNS), 用于基于WIFI连接的服务发现(NSD, Network Service Discovery)
  • fwmarkd: iptables的(fwmark)策略路由的配置(策略路由, 如设置网络权限, 连接打标签等

总的说来, netd进程在Android中间层服务NetworkManagementService以及内核之间建立了一个沟通的桥梁。

1.netd的启动与初始化

netd进程启动时, 主要处理做以下事情:

  • 创建一个NetlinkManager, 用于管理与内核通信的netlink连接
  • 初始化网络控制类, 如路由控制RouteController, 带宽控制BandwidthController
  • 启动各类事件监听类: DnsProxyListener监听DNS代理; CommandListener监听来自NetworkManagement的指令
  • 启动NetdHwService, 为HAL层提供接口

android netd,python,网络,数据库

int main() {
    using android::net::gCtls;
    Stopwatch s;

    ALOGI("Netd 1.0 starting");
    remove_pid_file();

    blockSigpipe();

    // Before we do anything that could fork, mark CLOEXEC the UNIX sockets that we get from init.
    // FrameworkListener does this on initialization as well, but we only initialize these
    // components after having initialized other subsystems that can fork.
    for (const auto& sock : { CommandListener::SOCKET_NAME,
                              DnsProxyListener::SOCKET_NAME,
                              FwmarkServer::SOCKET_NAME,
                              MDnsSdListener::SOCKET_NAME }) {
        setCloseOnExec(sock);
    }

    NetlinkManager *nm = NetlinkManager::Instance();
    if (nm == nullptr) {
        ALOGE("Unable to create NetlinkManager");
        exit(1);
    };

    gCtls = new android::net::Controllers();
    gCtls->init();

    CommandListener cl;
    nm->setBroadcaster((SocketListener *) &cl);

    if (nm->start()) {
        ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
        exit(1);
    }

    std::unique_ptr<NFLogListener> logListener;
    {
        auto result = makeNFLogListener();
        if (!isOk(result)) {
            ALOGE("Unable to create NFLogListener: %s", toString(result).c_str());
            exit(1);
        }
        logListener = std::move(result.value());
        auto status = gCtls->wakeupCtrl.init(logListener.get());
        if (!isOk(result)) {
            ALOGE("Unable to init WakeupController: %s", toString(result).c_str());
            // We can still continue without wakeup packet logging.
        }
    }

    // Set local DNS mode, to prevent bionic from proxying
    // back to this service, recursively.
    setenv("ANDROID_DNS_MODE", "local", 1);
    DnsProxyListener dpl(&gCtls->netCtrl, &gCtls->eventReporter);
    if (dpl.startListener()) {
        ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
        exit(1);
    }

    MDnsSdListener mdnsl;
    if (mdnsl.startListener()) {
        ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
        exit(1);
    }

    FwmarkServer fwmarkServer(&gCtls->netCtrl, &gCtls->eventReporter, &gCtls->trafficCtrl);
    if (fwmarkServer.startListener()) {
        ALOGE("Unable to start FwmarkServer (%s)", strerror(errno));
        exit(1);
    }

    Stopwatch subTime;
    status_t ret;
    if ((ret = NetdNativeService::start()) != android::OK) {
        ALOGE("Unable to start NetdNativeService: %d", ret);
        exit(1);
    }
    ALOGI("Registering NetdNativeService: %.1fms", subTime.getTimeAndReset());

    /*
     * Now that we're up, we can respond to commands. Starting the listener also tells
     * NetworkManagementService that we are up and that our binder interface is ready.
     */
    if (cl.startListener()) {
        ALOGE("Unable to start CommandListener (%s)", strerror(errno));
        exit(1);
    }
    ALOGI("Starting CommandListener: %.1fms", subTime.getTimeAndReset());

    write_pid_file();

    // Now that netd is ready to process commands, advertise service
    // availability for HAL clients.
    NetdHwService mHwSvc;
    if ((ret = mHwSvc.start()) != android::OK) {
        ALOGE("Unable to start NetdHwService: %d", ret);
        exit(1);
    }
    ALOGI("Registering NetdHwService: %.1fms", subTime.getTimeAndReset());

    ALOGI("Netd started in %dms", static_cast<int>(s.timeTaken()));

    IPCThreadState::self()->joinThreadPool();

    ALOGI("Netd exiting");

    remove_pid_file();

    exit(0);
}

android netd,python,网络,数据库

  CommandListener用于接收处理来自上层NetworkManagementService指令, 在netd启动时, 会监听netd这个socket, 并允许最多4个客户端请求的处理,netd启动完成后, 就可以处理来自中间层的指令请求以及与内核进行交互了。

2.netd与NetworkManagerService的交互

android netd,python,网络,数据库

  SystemServer进程启动时, 创建NetworkManagementService(以下简称(NMS)), 此时NMS会主动与netd建立socket链接:

android netd,python,网络,数据库

// SystemServer.java
       if (!disableNetwork) {
           traceBeginAndSlog("StartNetworkManagementService");
           try {
               networkManagement = NetworkManagementService.create(context);
               ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
           } catch (Throwable e) {
               reportWtf("starting NetworkManagement Service", e);
           }
           traceEnd();
       }

android netd,python,网络,数据库

创建NMS时, 启动一个新的线程用于与netd通信,

android netd,python,网络,数据库

 static NetworkManagementService create(Context context, String socket)
        throws InterruptedException {
    final NetworkManagementService service = new NetworkManagementService(context, socket);
    final CountDownLatch connectedSignal = service.mConnectedSignal;
    if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
    service.mThread.start();
    if (DBG) Slog.d(TAG, "Awaiting socket connection");
    connectedSignal.await();
    service.connectNativeNetdService();

    return service;
    }
    

       private NetworkManagementService(Context context, String socket) {
    mContext = context;

    // make sure this is on the same looper as our NativeDaemonConnector for sync purposes
    mFgHandler = new Handler(FgThread.get().getLooper());

    // Don't need this wake lock, since we now have a time stamp for when
    // the network actually went inactive.  (It might be nice to still do this,
    // but I don't want to do it through the power manager because that pollutes the
    // battery stats history with pointless noise.)
    //PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLock wl = null; //pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NETD_TAG);

    mConnector = new NativeDaemonConnector(
        new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl,
        FgThread.get().getLooper());
    mThread = new Thread(mConnector, NETD_TAG);

    mDaemonHandler = new Handler(FgThread.get().getLooper());

    // Add ourself to the Watchdog monitors.
    Watchdog.getInstance().addMonitor(this);

    LocalServices.addService(NetworkManagementInternal.class, new LocalService());

    synchronized (mTetheringStatsProviders) {
        mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "netd");
    }
}

android netd,python,网络,数据库

NMS通过NativeDaemonConnectornetd建立socket通信, NativeDaemonConnector主要做两个事情:

  • netd建立一个数据链接
  • 不断读取socket中的数据流: 一种是netd主动上报的命令, 一种是NMS发送给netd后的指令的响应

    android netd,python,网络,数据库

    @Override
    public void run() {
        mCallbackHandler = new Handler(mLooper, this);
    
        while (true) {
            try {
                listenToSocket();
            } catch (Exception e) {
                loge("Error in NativeDaemonConnector: " + e);
                SystemClock.sleep(5000);
            }
        }
    }
    
    
        private void listenToSocket() throws IOException {
        LocalSocket socket = null;
    
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = determineSocketAddress();
    
            socket.connect(address);
    
            InputStream inputStream = socket.getInputStream();
            synchronized (mDaemonLock) {
                mOutputStream = socket.getOutputStream();
            }
    
            mCallbacks.onDaemonConnected();
    
            FileDescriptor[] fdList = null;
            byte[] buffer = new byte[BUFFER_SIZE];
            int start = 0;
    
            while (true) {
                int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
                if (count < 0) {
                    loge("got " + count + " reading with start = " + start);
                    break;
                }
                fdList = socket.getAncillaryFileDescriptors();
    
                // Add our starting point to the count and reset the start.
                count += start;
                start = 0;
    
                for (int i = 0; i < count; i++) {
                    if (buffer[i] == 0) {
                        // Note - do not log this raw message since it may contain
                        // sensitive data
                        final String rawEvent = new String(
                                buffer, start, i - start, StandardCharsets.UTF_8);
    
                        boolean releaseWl = false;
                        try {
                            final NativeDaemonEvent event =
                                    NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
    
                            log("RCV <- {" + event + "}");
    
                            if (event.isClassUnsolicited()) {
                                Message msg = mCallbackHandler.obtainMessage(
                                        event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
                                if (mCallbackHandler.sendMessage(msg)) {
                                    releaseWl = false;
                                }
                            } else {
                                mResponseQueue.add(event.getCmdNumber(), event);
                            }
                        } catch (IllegalArgumentException e) {
                            log("Problem parsing message " + e);
                        } finally {
                            if (releaseWl) {
                                mWakeLock.release();
                            }
                        }
    
                        start = i + 1;
                    }
                }
    
                // We should end at the amount we read. If not, compact then
                // buffer and read again.
                if (start != count) {
                    final int remaining = BUFFER_SIZE - start;
                    System.arraycopy(buffer, start, buffer, 0, remaining);
                    start = remaining;
                } else {
                    start = 0;
                }
            }
        } catch (IOException ex) {
            loge("Communications error: " + ex);
            throw ex;
        } finally {
            synchronized (mDaemonLock) {
                if (mOutputStream != null) {
                    try {
                        loge("closing stream for " + mSocket);
                        mOutputStream.close();
                    } catch (IOException e) {
                        loge("Failed closing output stream: " + e);
                    }
                    mOutputStream = null;
                }
            }
    
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException ex) {
                loge("Failed closing socket: " + ex);
            }
        }
    }

    android netd,python,网络,数据库

    socket链接建立完成之后, NMS与netd可以相互通信, 发送指令与数据了. NMS通过NativeDaemonConnector执行相应的指令, 比如NMS设置网络接口的配置(打开/关闭网口):

android netd,python,网络,数据库

@Override
public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    LinkAddress linkAddr = cfg.getLinkAddress();
    if (linkAddr == null || linkAddr.getAddress() == null) {
        throw new IllegalStateException("Null LinkAddress given");
    }

    final Command cmd = new Command("interface", "setcfg", iface,
            linkAddr.getAddress().getHostAddress(),
            linkAddr.getPrefixLength());
    for (String flag : cfg.getFlags()) {
        cmd.appendArg(flag);
    }

    try {
        mConnector.execute(cmd);
    } catch (NativeDaemonConnectorException e) {
        throw e.rethrowAsParcelableException();
    }

android netd,python,网络,数据库

NativeDaemonConnector会将每个指令都指定一个唯一的序列, 并将其响应放到一个阻塞队列, 等待netd返回指令的结果, 如果超过指定的超时时间, 则抛出一个超时的异常.

在第一部分时, 讲到SocketListener拿到上层发过来的指令后, 会将其分发给对应的指令类进行处理(看SocketListener的子类FrameworkListener):

3.NETD与内核进行交互

NETD通过netlink事件与内核进行消息的交换.在第一部分时看到, netd启动时, 会配置socket与内核进行通信:

  • netlink事件NETLINK_KOBJECT_UEVENT: 用于内核向netd发生消息, 如网口的状态变化;
  • netlink事件NETLINK_ROUTE:用于接收路由信息, 如路由表的更新与删除;
  • netlink事件NETLINK_NFLOG:用于接收数据流量使用配额的消息, 如数据使用超限;
  • netlink事件NETLINK_NETFILTER用于接收包过滤(netfilter)的消息;

    android netd,python,网络,数据库

    int NetlinkManager::start() {
        if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
         0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII, false)) == NULL) {
        return -1;
        }
    
        if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,
                                     RTMGRP_LINK |
                                     RTMGRP_IPV4_IFADDR |
                                     RTMGRP_IPV6_IFADDR |
                                     RTMGRP_IPV6_ROUTE |
                                     (1 << (RTNLGRP_ND_USEROPT - 1)),
         NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
        return -1;
        }
    
        if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
            NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
        ALOGW("Unable to open qlog quota socket, check if xt_quota2 can send via UeventHandler");
        // TODO: return -1 once the emulator gets a new kernel.
        }
    
        if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,
            0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST, true)) == NULL) {
        ALOGE("Unable to open strict socket");
        // TODO: return -1 once the emulator gets a new kernel.
        }
    
        return 0;
    }

    android netd,python,网络,数据库

    每一个netlink的socket都会新建一个NetlinkHandler, 用于处理内核的消息, 并将该消息广播给上层:

    android netd,python,网络,数据库

    void NetlinkHandler::onEvent(NetlinkEvent *evt) {
        const char *subsys = evt->getSubsystem();
        if (!subsys) {
        ALOGW("No subsystem found in netlink event");
        return;
        }
    
        if (!strcmp(subsys, "net")) {
        NetlinkEvent::Action action = evt->getAction();
        const char *iface = evt->findParam("INTERFACE");
    
        if (action == NetlinkEvent::Action::kAdd) {
            notifyInterfaceAdded(iface);
        } else if (action == NetlinkEvent::Action::kRemove) {
            notifyInterfaceRemoved(iface);
        } else if (action == NetlinkEvent::Action::kChange) {
            evt->dump();
            notifyInterfaceChanged("nana", true);
        } else if (action == NetlinkEvent::Action::kLinkUp) {
            notifyInterfaceLinkChanged(iface, true);
        } else if (action == NetlinkEvent::Action::kLinkDown) {
            notifyInterfaceLinkChanged(iface, false);
        } else if (action == NetlinkEvent::Action::kAddressUpdated ||
                   action == NetlinkEvent::Action::kAddressRemoved) {
            const char *address = evt->findParam("ADDRESS");
            const char *flags = evt->findParam("FLAGS");
            const char *scope = evt->findParam("SCOPE");
            if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {
                // Note: if this interface was deleted, iface is "" and we don't notify.
                SockDiag sd;
                if (sd.open()) {
                    char addrstr[INET6_ADDRSTRLEN];
                    strncpy(addrstr, address, sizeof(addrstr));
                    char *slash = strchr(addrstr, '/');
                    if (slash) {
                        *slash = '\0';
                    }
    
                    int ret = sd.destroySockets(addrstr);
                    if (ret < 0) {
                        ALOGE("Error destroying sockets: %s", strerror(ret));
                    }
                } else {
                    ALOGE("Error opening NETLINK_SOCK_DIAG socket: %s", strerror(errno));
                }
            }
            if (iface && iface[0] && address && flags && scope) {
                notifyAddressChanged(action, address, iface, flags, scope);
            }
        } else if (action == NetlinkEvent::Action::kRdnss) {
            const char *lifetime = evt->findParam("LIFETIME");
            const char *servers = evt->findParam("SERVERS");
            if (lifetime && servers) {
                notifyInterfaceDnsServers(iface, lifetime, servers);
            }
        } else if (action == NetlinkEvent::Action::kRouteUpdated ||
                   action == NetlinkEvent::Action::kRouteRemoved) {
            const char *route = evt->findParam("ROUTE");
            const char *gateway = evt->findParam("GATEWAY");
            const char *iface = evt->findParam("INTERFACE");
            if (route && (gateway || iface)) {
                notifyRouteChange(action, route, gateway, iface);
            }
        }
    
        } else if (!strcmp(subsys, "qlog") || !strcmp(subsys, "xt_quota2")) {
        const char *alertName = evt->findParam("ALERT_NAME");
        const char *iface = evt->findParam("INTERFACE");
        notifyQuotaLimitReached(alertName, iface);
    
        } else if (!strcmp(subsys, "strict")) {
        const char *uid = evt->findParam("UID");
        const char *hex = evt->findParam("HEX");
        notifyStrictCleartext(uid, hex);
    
        } else if (!strcmp(subsys, "xt_idletimer")) {
        const char *label = evt->findParam("INTERFACE");
        const char *state = evt->findParam("STATE");
        const char *timestamp = evt->findParam("TIME_NS");
        const char *uid = evt->findParam("UID");
        if (state)
            notifyInterfaceClassActivity(label, !strcmp("active", state),
                                         timestamp, uid);
        }
    }

    android netd,python,网络,数据库

    监听内核网络状态并回调:

    android netd,python,网络,数据库

        private void listenToSocket() throws IOException {
            LocalSocket socket = null;
    
            try {
                socket = new LocalSocket();
                LocalSocketAddress address = determineSocketAddress();
    
                socket.connect(address);
    
                InputStream inputStream = socket.getInputStream();
                synchronized (mDaemonLock) {
                    mOutputStream = socket.getOutputStream();
                }
    
                mCallbacks.onDaemonConnected();
    
                FileDescriptor[] fdList = null;
                byte[] buffer = new byte[BUFFER_SIZE];
                int start = 0;
    
                while (true) {
                    int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
                    if (count < 0) {
                        loge("got " + count + " reading with start = " + start);
                        break;
                    }
                    fdList = socket.getAncillaryFileDescriptors();
    
                    // Add our starting point to the count and reset the start.
                    count += start;
                    start = 0;
    
                    for (int i = 0; i < count; i++) {
                        if (buffer[i] == 0) {
                            // Note - do not log this raw message since it may contain
                            // sensitive data
                            final String rawEvent = new String(
                                    buffer, start, i - start, StandardCharsets.UTF_8);
    
                            boolean releaseWl = false;
                            try {
                                final NativeDaemonEvent event =
                                        NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
    
                                log("RCV <- {" + event + "}");
    
                                if (event.isClassUnsolicited()) {
                                    // TODO: migrate to sending NativeDaemonEvent instances
                                    if (mCallbacks.onCheckHoldWakeLock(event.getCode())
                                            && mWakeLock != null) {
                                        mWakeLock.acquire();
                                        releaseWl = true;
                                    }
                                    Message msg = mCallbackHandler.obtainMessage(
                                            event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
                                    if (mCallbackHandler.sendMessage(msg)) {
                                        releaseWl = false;
                                    }
                                } else {
                                    mResponseQueue.add(event.getCmdNumber(), event);
                                }
                            } catch (IllegalArgumentException e) {
                                log("Problem parsing message " + e);
                            } finally {
                                if (releaseWl) {
                                    mWakeLock.release();
                                }
                            }
    
                            start = i + 1;
                        }
                    }
    
                    if (start == 0) {
                        log("RCV incomplete");
                    }
    
                    // We should end at the amount we read. If not, compact then
                    // buffer and read again.
                    if (start != count) {
                        final int remaining = BUFFER_SIZE - start;
                        System.arraycopy(buffer, start, buffer, 0, remaining);
                        start = remaining;
                    } else {
                        start = 0;
                    }
                }
            } catch (IOException ex) {
                loge("Communications error: " + ex);
                throw ex;
            } finally {
                synchronized (mDaemonLock) {
                    if (mOutputStream != null) {
                        try {
                            loge("closing stream for " + mSocket);
                            mOutputStream.close();
                        } catch (IOException e) {
                            loge("Failed closing output stream: " + e);
                        }
                        mOutputStream = null;
                    }
                }
    
                try {
                    if (socket != null) {
                        socket.close();
                    }
                } catch (IOException ex) {
                    loge("Failed closing socket: " + ex);
                }
            }
        }

    android netd,python,网络,数据库

    回调到NetworkManagementService.java中的onEvent方法并通知之前创建好的Observers

    android netd,python,网络,数据库

            @Override
            public boolean onEvent(int code, String raw, String[] cooked) {
                String errorMessage = String.format("Invalid event from daemon (%s)", raw);
                switch (code) {
                case NetdResponseCode.InterfaceChange:
                        /*
                         * a network interface change occured
                         * Format: "NNN Iface added <name>"
                         *         "NNN Iface removed <name>"
                         *         "NNN Iface changed <name> <up/down>"
                         *         "NNN Iface linkstatus <name> <up/down>"
                         */
                        if (cooked.length < 4 || !cooked[1].equals("Iface")) {
                            throw new IllegalStateException(errorMessage);
                        }
                        if (cooked[2].equals("added")) {
                            notifyInterfaceAdded(cooked[3]);
                            return true;
                        } else if (cooked[2].equals("removed")) {
                            notifyInterfaceRemoved(cooked[3]);
                            return true;
                        } else if (cooked[2].equals("changed") && cooked.length == 5) {
                            notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
                            return true;
                        } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
                            notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
                            return true;
                        }
                        throw new IllegalStateException(errorMessage);
                        // break;
                case NetdResponseCode.BandwidthControl:
                        /*
                         * Bandwidth control needs some attention
                         * Format: "NNN limit alert <alertName> <ifaceName>"
                         */
                        if (cooked.length < 5 || !cooked[1].equals("limit")) {
                            throw new IllegalStateException(errorMessage);
                        }
                        if (cooked[2].equals("alert")) {
                            notifyLimitReached(cooked[3], cooked[4]);
                            return true;
                        }
                        throw new IllegalStateException(errorMessage);
                        // break;
                case NetdResponseCode.InterfaceClassActivity:
                        /*
                         * An network interface class state changed (active/idle)
                         * Format: "NNN IfaceClass <active/idle> <label>"
                         */
                        if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) {
                            throw new IllegalStateException(errorMessage);
                        }
                        long timestampNanos = 0;
                        int processUid = -1;
                        if (cooked.length >= 5) {
                            try {
                                timestampNanos = Long.parseLong(cooked[4]);
                                if (cooked.length == 6) {
                                    processUid = Integer.parseInt(cooked[5]);
                                }
                            } catch(NumberFormatException ne) {}
                        } else {
                            timestampNanos = SystemClock.elapsedRealtimeNanos();
                        }
                        boolean isActive = cooked[2].equals("active");
                        notifyInterfaceClassActivity(Integer.parseInt(cooked[3]),
                                isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
                                : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
                                timestampNanos, processUid, false);
                        return true;
                        // break;
                case NetdResponseCode.InterfaceAddressChange:
                        /*
                         * A network address change occurred
                         * Format: "NNN Address updated <addr> <iface> <flags> <scope>"
                         *         "NNN Address removed <addr> <iface> <flags> <scope>"
                         */
                        if (cooked.length < 7 || !cooked[1].equals("Address")) {
                            throw new IllegalStateException(errorMessage);
                        }
    
                        String iface = cooked[4];
                        LinkAddress address;
                        try {
                            int flags = Integer.parseInt(cooked[5]);
                            int scope = Integer.parseInt(cooked[6]);
                            address = new LinkAddress(cooked[3], flags, scope);
                        } catch(NumberFormatException e) {     // Non-numeric lifetime or scope.
                            throw new IllegalStateException(errorMessage, e);
                        } catch(IllegalArgumentException e) {  // Malformed/invalid IP address.
                            throw new IllegalStateException(errorMessage, e);
                        }
    
                        if (cooked[2].equals("updated")) {
                            notifyAddressUpdated(iface, address);
                        } else {
                            notifyAddressRemoved(iface, address);
                        }
                        return true;
                        // break;
                case NetdResponseCode.InterfaceDnsServerInfo:
                        /*
                         * Information about available DNS servers has been received.
                         * Format: "NNN DnsInfo servers <interface> <lifetime> <servers>"
                         */
                        long lifetime;  // Actually a 32-bit unsigned integer.
    
                        if (cooked.length == 6 &&
                            cooked[1].equals("DnsInfo") &&
                            cooked[2].equals("servers")) {
                            try {
                                lifetime = Long.parseLong(cooked[4]);
                            } catch (NumberFormatException e) {
                                throw new IllegalStateException(errorMessage);
                            }
                            String[] servers = cooked[5].split(",");
                            notifyInterfaceDnsServerInfo(cooked[3], lifetime, servers);
                        }
                        return true;
                        // break;
                case NetdResponseCode.RouteChange:
                        /*
                         * A route has been updated or removed.
                         * Format: "NNN Route <updated|removed> <dst> [via <gateway] [dev <iface>]"
                         */
                        if (!cooked[1].equals("Route") || cooked.length < 6) {
                            throw new IllegalStateException(errorMessage);
                        }
    
                        String via = null;
                        String dev = null;
                        boolean valid = true;
                        for (int i = 4; (i + 1) < cooked.length && valid; i += 2) {
                            if (cooked[i].equals("dev")) {
                                if (dev == null) {
                                    dev = cooked[i+1];
                                } else {
                                    valid = false;  // Duplicate interface.
                                }
                            } else if (cooked[i].equals("via")) {
                                if (via == null) {
                                    via = cooked[i+1];
                                } else {
                                    valid = false;  // Duplicate gateway.
                                }
                            } else {
                                valid = false;      // Unknown syntax.
                            }
                        }
                        if (valid) {
                            try {
                                // InetAddress.parseNumericAddress(null) inexplicably returns ::1.
                                InetAddress gateway = null;
                                if (via != null) gateway = InetAddress.parseNumericAddress(via);
                                RouteInfo route = new RouteInfo(new IpPrefix(cooked[3]), gateway, dev);
                                notifyRouteChange(cooked[2], route);
                                return true;
                            } catch (IllegalArgumentException e) {}
                        }
                        throw new IllegalStateException(errorMessage);
                        // break;
                case NetdResponseCode.StrictCleartext:
                    final int uid = Integer.parseInt(cooked[1]);
                    final byte[] firstPacket = HexDump.hexStringToByteArray(cooked[2]);
                    try {
                        ActivityManager.getService().notifyCleartextNetwork(uid, firstPacket);
                    } catch (RemoteException ignored) {
                    }
                    break;
                default: break;
                }
                return false;
            }
        }

    android netd,python,网络,数据库

三、Netd测试工具ndc

ndc的原理其实就是通过socket连接上netd进行交互,这部分可以从源代码体现:

ndc.c

android netd,python,网络,数据库

int main(int argc, char **argv) {
    //argv[1]可以是socket name.
    if ((sock = socket_local_client(argv[1],
                                     ANDROID_SOCKET_NAMESPACE_RESERVED,
                                     SOCK_STREAM)) < 0) {
        //如果不传,那么默认就是name为"netd"的socket                   
        if ((sock = socket_local_client("netd",
                                         ANDROID_SOCKET_NAMESPACE_RESERVED,
                                         SOCK_STREAM)) < 0) {
            fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
            exit(4);
        }
    }
    exit(do_cmd(sock, argc-cmdOffset, &(argv[cmdOffset])));
} 

android netd,python,网络,数据库

android netd,python,网络,数据库

static int do_cmd(int sock, int argc, char **argv) {
   //命令参数最终通过socket发送给netd服务进程处理
   if (write(sock, final_cmd, strlen(final_cmd) + 1) < 0) {
        int res = errno;
        perror("write");
        free(final_cmd);
        return res;
    }
}

android netd,python,网络,数据库

监听:

android netd,python,网络,数据库

查看可用命令表:

android netd,python,网络,数据库

console:/ # ndc interface list
110 0 dummy0
110 0 eth0
110 0 ip6_vti0
110 0 ip6tnl0
110 0 ip_vti0
110 0 lo
200 0 Interface list completed

android netd,python,网络,数据库

  例如: $ adb shell ndc interface list

 文章来源地址https://www.toymoban.com/news/detail-769427.html

interface list
readrxcounter| readtxcounter
getthrottle<iface><”rx|tx”>
setthrottle<iface><rx_kbps|tx_kbps>
driver<iface><cmd><args>
route<add|remove> <iface> <”default|secondary”><dst> <prefix> <gateway>
list_ttys
ipfwd status
enable|disable
tether status
start-reverse|stop-reverse
stop<
start<addr_1 addr_2 addr_3 addr_4 [addr_2n]>
interface<add|remove|list>
dnslist
dnsset <addr_1> < addr_2>
nat <enable|disable><iface><extface><addrcnt><nated-ipaddr/prelength>
pppd attach<tty> <addr_local> <add_remote> <dns_1><dns_2>
detach<tty>
softap startap|stopap
fwreload<iface> <AP|P2P>
clients
status
set<iface> <SSID> <wpa-psk|wpa2-psk|open> [<key><channel> <preamble><max SCB>]
resolver setdefaultif<iface>
setifdns<iface><dns_1><dns_2>
flushdefaultif
flushif<iface>
bandwith enable|disable
removequota|rq
getquota|gq
getiquota|giq<iface>
setquota|sq<bytes> <iface>
removequota|rqs<iface>
removeiiquota|riq<iface>
setiquota|sq<interface><bytes>
addnaughtyapps|ana<appUid>
removenaughtyapps|rna<appUid>
setgolbalalert|sga<bytes>
debugsettetherglobalalert|dstga<iface0><iface1>
setsharedalert|ssa<bytes>
removesharedalert|rsa
setinterfacealert|sia<iface><bytes>
removeinterfacealert|ria<iface>
gettetherstats|gts<iface0><iface1>
idletimer enable|disable
add|remove<iface><timeout><classLabel>
firewall enable|disable|is_enabled
set_interface_rule<rmnet0><allow|deny>
set_egress_source_rule<ip_addr><allow|deny>
set_egress_dest_rule<ip_addr><port><allow|deny>
set_uid_rule<uid><allow|deny>
clatd stop|status|start<iface>
 

到了这里,关于Android 9.0 网络之netd详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 〖Python网络爬虫实战㉒〗- 数据存储之数据库详解

    订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000+                 python项目实战                  Python编程基础教程系列(零基础小白搬砖逆袭) 说明:本专栏持续更新中,目前专栏免费订阅,在转为付费专栏前订阅本专栏的,可以免费订阅付费专栏,

    2024年02月06日
    浏览(48)
  • Android studio 通过mysql连接数据库完成注册登录,登陆后通过tcp协议与电脑的网络调试助手互发信息

    先不多直接看软件截图 这个是首页等陆界面 xml代码如下 MainActivity文件    注册界面的xlm文件  这个界面比较简单就不介绍了   MainActivity文件 这是一个注册失败的界面,如果数据库内有相同的账号密码则显示注册失败 话不多说直接上代码 MainActivity里面的代码 这里是user类用

    2024年02月02日
    浏览(46)
  • Android 9.0 蓝牙功能之一:蓝牙设置

    本章节记录如何构建蓝牙设置。 注意蓝牙应用必须是 System App。 LocalBluetoothManager 是操作蓝牙的主要入口。 1.通过 LocalBluetoothManager,可以获取到LocalBluetoothAdapter;CachedBluetoothDeviceManager;BluetoothEventManager、LocalBluetoothProfileManager。 2.通过 BluetoothEventManager.registerCallback 注册回调,

    2023年04月24日
    浏览(44)
  • android 9.0 屏蔽系统所有通知功能实现

    目录 1.概述 2.屏蔽系统所有通知功能实现的核心类

    2024年02月11日
    浏览(47)
  • Android 9.0 Vold挂载流程解析(上)

    我们分2篇文章来介绍Android 9.0中存储卡的挂载流程,本篇文章先介绍总体的挂载模块、Vold进程的入口main函数的详细分析,有了这些基础知识,下一篇中我们再详细介绍收到驱动层消息是怎么挂载和卸载存储卡的,还有framework层如何与vold进程通讯交流。Android 9.0 Vold挂载流程解

    2024年02月12日
    浏览(50)
  • Android 9.0 系统关闭时区夏令时功能

    在9.0的系统rom定制化开发中,在开发国际化项目中,对于有些地区执行夏令时,导致时间对不上,也有可能 一个国家某些地区执行夏令时而一些国家不执行夏令时,导致时间不一样,所以为了避免麻烦,就关闭系统的夏令时功能, 夏令时会比正常时间晚一小时。这就需要分

    2023年04月16日
    浏览(54)
  • Android 9.0 无源码app增加授予相关权限

    在9.0的系统rom产品开发中,对于一些无源码app需要增加一些权限,比如悬浮窗权限,由于app内部没申请这个权限,所以需要系统适配默认授予这个权限, 就需要在PMS解析安装app的时候 授予悬浮窗权限就可以了 在pms管理解析安装app中,是通过PackageManage的getPackageArchiveInfo()实

    2024年02月02日
    浏览(43)
  • Android 9.0以上 连接wifi失败 解决方案

    Android9开始,WIFI那块的API发生了改变: addNetwork(WifiConfiguration config) 此方法在API级别Q中已弃用) 见addNetworkSuggestions(java.util.List), removeNetworkSuggestions(java.util.List)新的API添加Wi-Fi网络进行审议时,自动连接到无线网络。 兼容性注意:对于目标Build.VERSION_CODES.Q或更高的应用程序

    2024年02月12日
    浏览(41)
  • Android 9.0 禁用adb shell input输入功能

    在9.0的系统rom产品开发中,在进行一些定制开发中,对于一些adb shell功能需要通过属性来控制禁止使用input 等输入功能,比如adb shell input keyevent 响应输入事件等,所以就需要 熟悉adb shell input的输入事件流程,然后来禁用adb shell input的输入事件功能,接下来分析下adb shell inp

    2024年02月20日
    浏览(52)
  • Android 9.0 系统开机自启动第三方app

      在9.0的系统rom定制化开发中,在framework定制话的功能开发中,在内置的app中,有时候在系统开机以后会要求启动第三方app的功能,所以这就需要在监听开机完成的广播,然后在启动第三方app就可以了,接下来就需要在系统类中监听开机完成的广播流程来实现功能 在开机完成

    2024年02月11日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包