Android P 9.0 增加以太网静态IP功能

news2024/11/26 0:54:20

效果图

 一、Settings添加以太网的配置:

1、vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\network_and_internet.xml

<com.android.settingslib.RestrictedPreference
    android:key="ethernet_settings"
    android:title="@string/ethernet_settings_title"
    android:summary="@string/summary_placeholder"
    android:icon="@drawable/ic_ethernet_cell"
    android:fragment="com.android.settings.ethernet.EthernetSettings"
    android:order="-17"/>

在 mobile_network_settings 和 tether_settings 之间增加如上代码,

对应的 icon 资源文件是我从 SystemUI 中拷贝过来的,稍微调整了下大小,也贴给你们吧

2、vendor\mediatek\proprietary\packages\apps\MtkSettings\res\drawable\ic_ethernet_cell.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:autoMirrored="true"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="48"
    android:viewportHeight="48"
    android:tint="?android:attr/colorControlNormal">
    <path
        android:fillColor="#fff"
        android:pathData="M15.54 13.52l-3.08-2.55L1.64 24l10.82 13.04 3.08-2.55L6.84 24l8.7-10.48zM14 26h4v-4h-4v4zm20-4h-4v4h4v-4zm-12 4h4v-4h-4v4zm13.54-15.04l-3.08 2.55L41.16 24l-8.7 10.48 3.08 2.55L46.36 24 35.54 10.96z"/>
</vector>

vendor\mediatek\proprietary\packages\apps\MtkSettings\res\values\strings.xml

<string name="ethernet_ip_settings_invalid_ip">"Please fill in the correct format."</string>
<string name="eth_ip_settings_please_complete_settings">"Network information is not complete, please fill in the complete"</string>
<string name="save_satic_ethernet">"Save"</string>
<string name="enthernet_static">"Use static settings"</string>
<string name="enthernet_ip_address">"IP address"</string>
<string name="enthernet_gateway">"gateway"</string>
<string name="enthernet_netmask">"Subnet mask"</string>
<string name="enthernet_dns1">"domain1"</string>
<string name="enthernet_dns2">"domain2"</string>
<string name="ethernet_quick_toggle_title">"Ethernet"</string>
<string name="open_ethernet">"Open Ethernet"</string>
<string name="ethernet_static_ip_settings_title">"Setting Ethernet"</string>
<string name="ethernet_settings">"Ethernet"</string>
<string name="ethernet_settings_title">"Ethernet"</string>

vendor\mediatek\proprietary\packages\apps\MtkSettings\res\values-zh-rCN\strings.xml

<string name="ethernet_ip_settings_invalid_ip">"请填写正确的格式"</string>
<string name="save_satic_ethernet">"保存"</string>
<string name="eth_ip_settings_please_complete_settings">"网络信息不完整,请填写完整"</string>
<string name="enthernet_static">"使用静态设置"</string>
<string name="enthernet_ip_address">"IP地址"</string>
<string name="enthernet_gateway">"网关"</string>
<string name="enthernet_netmask">"子网掩码"</string>
<string name="enthernet_dns1">"域名1"</string>
<string name="enthernet_dns2">"域名2"</string>
<string name="ethernet_quick_toggle_title">"以太网"</string>
<string name="open_ethernet">"打开以太网"</string>
<string name="ethernet_static_ip_settings_title">"配置以太网"</string>
<string name="ethernet_settings">"以太网"</string>
<string name="ethernet_settings_title">"以太网"</string> 

3、增加对应设置的两个布局xml

vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\ethernet_settings.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    android:title="@string/ethernet_settings">

<SwitchPreference
    android:key="ethernet"
    android:title="@string/ethernet_quick_toggle_title"
    android:summary="@string/open_ethernet"/>

<PreferenceScreen
    android:dependency="ethernet"
    android:fragment="com.android.settings.ethernet.EthernetStaticIP"
    android:key="ethernet_static_ip"
    android:title="@string/ethernet_static_ip_settings_title" />
        
</PreferenceScreen>

vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\ethernet_static_ip.xml

	<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
        android:title="@string/ethernet_static_ip_settings_title">

    <SwitchPreference
            android:key="use_static_ip"
            android:title="@string/enthernet_static"
            android:persistent="false"/>    
       
    <EditTextPreference
            android:dependency="use_static_ip"
            android:key="ip_address"
            android:title="@string/enthernet_ip_address"
            android:persistent="false"
            android:singleLine="true"/>    
    
    <EditTextPreference
            android:dependency="use_static_ip"
            android:key="gateway"
            android:title="@string/enthernet_gateway"
            android:persistent="false"
            android:singleLine="true"/>    
    
    <EditTextPreference
            android:dependency="use_static_ip"
            android:key="netmask"
            android:title="@string/enthernet_netmask"
            android:persistent="false"
            android:singleLine="true" />    

    <EditTextPreference
            android:dependency="use_static_ip"
            android:key="dns1"
            android:title="@string/enthernet_dns1"
            android:persistent="false"
            android:singleLine="true"/>    
    
    <EditTextPreference
            android:dependency="use_static_ip"
            android:key="dns2"
            android:title="@string/enthernet_dns2"
            android:persistent="false"
            android:singleLine="true"/>    
   
</PreferenceScreen>

4、增加对应两个布局的 java 控制类

vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\ethernet\EthernetSettings.java

package com.android.settings.ethernet;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v14.preference.SwitchPreference;
import android.provider.Settings;
import android.provider.Settings.System;
import android.provider.Settings.Secure;
import android.util.Log;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.net.InetAddress;

import android.net.EthernetManager;
import android.net.StaticIpConfiguration;
import android.net.LinkAddress;
import android.net.IpConfiguration;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.R;

public class EthernetSettings extends SettingsPreferenceFragment 
		implements Preference.OnPreferenceChangeListener{
			
    private static final String TAG = "EthernetSettings";

    private static final String USE_ETHERNET_SETTINGS = "ethernet";
	
    public static final String IS_ETHERNET_OPEN = Settings.IS_ETHERNET_OPEN;	
    private SwitchPreference mUseEthernet;
    private IntentFilter mIntentFilter;

    private boolean isEthernetEnabled() {
		return Settings.System.getInt(getActivity().getContentResolver(), IS_ETHERNET_OPEN,0) == 1 ? true : false;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.ethernet_settings);
		mUseEthernet = (SwitchPreference) findPreference(USE_ETHERNET_SETTINGS);
		mUseEthernet.setOnPreferenceChangeListener(this);
		if(isEthernetEnabled()) {
		   mUseEthernet.setChecked(true);
		} else {
		   mUseEthernet.setChecked(false);
		}
		File f = new File("sys/class/net/eth0/address");
		if (f.exists()) {
		   mUseEthernet.setEnabled(true);		
		} else {
		   mUseEthernet.setEnabled(false);
		}
    }
	
    @Override
    public void onResume() {
        super.onResume();
    }

     @Override
     public int getMetricsCategory(){return MetricsEvent.ETHERNET;}

     @Override
     public void onPause() {
        super.onPause();
    }	

    @Override
    public boolean onPreferenceChange(Preference preference, Object value) {
    	boolean result = true;
    	final String key = preference.getKey();
    	if (USE_ETHERNET_SETTINGS.equals(key)) {
    		Settings.System.putInt(getActivity().getContentResolver(), IS_ETHERNET_OPEN, 
    			((Boolean) value) ? 1 : 0);
    	}

    	return result;
    }

}

vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\ethernet\EthernetStaticIP.java

	package com.android.settings.ethernet;
	
	import android.app.Dialog;
	import android.content.DialogInterface;
	import android.content.Intent;
	import android.content.ContentResolver;
	import android.os.Bundle;
	import android.support.v7.preference.Preference;
	import android.preference.PreferenceActivity;
	import android.support.v7.preference.PreferenceScreen;
	import android.support.v7.preference.CheckBoxPreference;
	import android.support.v7.preference.EditTextPreference;
	import android.support.v14.preference.SwitchPreference;
	
	import android.provider.Settings;
	import android.provider.Settings.Secure;
	import android.util.Log;
	import android.view.ContextMenu;
	import android.view.Menu;
	import android.view.MenuInflater;
	import android.view.MenuItem;
	import android.view.View;
	import android.view.ContextMenu.ContextMenuInfo;
	import android.widget.AdapterView;
	import android.widget.Toast;
	import android.widget.AdapterView.AdapterContextMenuInfo;
	
	import android.text.TextUtils;
	import java.util.Set;
	import java.util.WeakHashMap;
	import java.util.Formatter;
	import java.net.InetAddress;
	
	import android.net.EthernetManager;
	import android.net.StaticIpConfiguration;
	import android.net.LinkAddress;
	import android.net.IpConfiguration;
	import android.net.IpConfiguration.IpAssignment;
	import android.net.IpConfiguration.ProxySettings;
	import android.net.NetworkInfo.DetailedState;
	import android.content.BroadcastReceiver;
	import android.content.IntentFilter;
	import android.content.Context;
	import android.net.NetworkInfo;
	
	import android.view.KeyEvent;
	import android.view.Menu;
	import android.view.MenuItem;
	import android.app.AlertDialog;
	
	import com.android.settings.SettingsPreferenceFragment;
	import com.android.settings.R;
	import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
	
	public class EthernetStaticIP  extends SettingsPreferenceFragment 
	implements Preference.OnPreferenceChangeListener {
	    private static final String TAG = "EthernetStaticIP";
	    public static final boolean DEBUG = false;
	    private static void LOG(String msg) {
	        if ( DEBUG ) {
	            Log.d(TAG, msg);
	        }
	    }
	    
		/*-------------------------------------------------------*/
	    
	    private static final String KEY_USE_STATIC_IP = "use_static_ip";
	
	    private static final String KEY_IP_ADDRESS = "ip_address";
	    private static final String KEY_GATEWAY = "gateway";
	    private static final String KEY_NETMASK = "netmask";
	    private static final String KEY_DNS1 = "dns1";
	    private static final String KEY_DNS2 = "dns2";
	    public static final String ETHERNET_USE_STATIC_IP = Settings.IS_ETHERNET_STATUC_OPEN;
	    
	    private static final int MENU_ITEM_SAVE = Menu.FIRST;
	    private static final int MENU_ITEM_CANCEL = Menu.FIRST + 1;
	    
	    private String[] mSettingNames = {
	        Settings.ETHERNET_STATIC_IP, 
	        Settings.ETHERNET_STATIC_GATEWAY,
	        Settings.ETHERNET_STATIC_NETMASK,
	        Settings.ETHERNET_STATIC_DNS1, 
	        Settings.ETHERNET_STATIC_DNS2
	    };
	    
	    
	    private String[] mPreferenceKeys = {
	        KEY_IP_ADDRESS,
	        KEY_GATEWAY,
	        KEY_NETMASK,
	        KEY_DNS1,
	        KEY_DNS2,
	    };
	    
		/*-------------------------------------------------------*/
	    
	    private SwitchPreference mUseStaticIpSwitch;
	    private StaticIpConfiguration mStaticIpConfiguration;
	    private IpConfiguration mIpConfiguration;
	    private EthernetManager mEthernetManager;
	    
	    private boolean isOnPause = false;
	    private boolean chageState = false;
	    
	    public EthernetStaticIP() {
	    }
	    
	    @Override
	    public void onActivityCreated(Bundle savedInstanceState){
	        super.onActivityCreated(savedInstanceState);
			
	        addPreferencesFromResource(R.xml.ethernet_static_ip);
	
	        mUseStaticIpSwitch = (SwitchPreference)findPreference(KEY_USE_STATIC_IP);
	        mUseStaticIpSwitch.setOnPreferenceChangeListener(this);
	  
	        for ( int i = 0; i < mPreferenceKeys.length; i++ ) {
	            Preference preference = findPreference(mPreferenceKeys[i] );
	            preference.setOnPreferenceChangeListener(this);
	        }
	        setHasOptionsMenu(true);
	    }
	    
	    @Override
	    public void onResume() {
	        super.onResume();
	        if(!isOnPause) {
	            updateIpSettingsInfo();
	        }
	        isOnPause = false;
	    }
	    
	    @Override
	    public int getMetricsCategory(){return MetricsEvent.ETHERNET_STATIC;}    
	    
	    @Override
	    public void onPause() {
	        isOnPause = true;
	        super.onPause();
	    }
	    
	    @Override
	    public void onDestroy() {
	        super.onDestroy();
	    }
	
	    @Override
	    public void onSaveInstanceState(Bundle outState) {
	        super.onSaveInstanceState(outState);
	    }
	    
	    @Override
	    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
	        menu.add(Menu.NONE, MENU_ITEM_SAVE, 0, R.string.save_satic_ethernet)
	                .setEnabled(true)
	                .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
	        super.onCreateOptionsMenu(menu, inflater);
	    }
	
	    @Override
	    public boolean onOptionsItemSelected(MenuItem item) {
	
	        switch (item.getItemId()) {
	        
	            case MENU_ITEM_SAVE:
	            	saveIpSettingsInfo();
			if(isIpDataInUiComplete())
	            	   finish();
	                return true;
	                
	            case MENU_ITEM_CANCEL:
	                finish();
	                return true;
	        }
	        
	        return super.onOptionsItemSelected(item);
	    }        
	    
	    
	    private void updateIpSettingsInfo() {
	    	LOG("Static IP status updateIpSettingsInfo");
	        ContentResolver contentResolver = getContentResolver();
	        
	        mUseStaticIpSwitch.setChecked(Settings.System.getInt(contentResolver, ETHERNET_USE_STATIC_IP, 0) != 0);
	        
	        for (int i = 0; i < mSettingNames.length; i++) {
	            EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]);
	            String settingValue = Settings.System.getString(contentResolver, mSettingNames[i]);
	            preference.setText(settingValue);
	            preference.setSummary(settingValue);
	        }
	    }
	        
	
	   
	    private void saveIpSettingsInfo() {
	        ContentResolver contentResolver = getContentResolver();
	  /*      
	        if(!chageState)   
	        	return;
	  */      
	        if(!isIpDataInUiComplete()) 
	        {     
	           Toast.makeText(getActivity(), R.string.eth_ip_settings_please_complete_settings, Toast.LENGTH_LONG).show();
	           return;
	        }
	
			mIpConfiguration = new IpConfiguration();
			mStaticIpConfiguration = new StaticIpConfiguration();
	        
	        for (int i = 0; i < mSettingNames.length; i++) {
	            
	            EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]);
	            String text = preference.getText();
		    try {
		    	switch (mPreferenceKeys[i]) {
			   case KEY_IP_ADDRESS:
			      	mStaticIpConfiguration.ipAddress = new LinkAddress(InetAddress.getByName(text), 24);
			      break;
			   case KEY_GATEWAY:
		    		mStaticIpConfiguration.gateway = InetAddress.getByName(text);
			      break;
			   case KEY_NETMASK:
				mStaticIpConfiguration.domains = text;
			      break;
			   case KEY_DNS1:
		    		mStaticIpConfiguration.dnsServers.add(InetAddress.getByName(text));
			      break;
			   case KEY_DNS2:
		    		mStaticIpConfiguration.dnsServers.add(InetAddress.getByName(text));
			      break;	
		        }            
		    } catch (Exception e) {
	                e.printStackTrace();
	            }
	            if ( null == text || TextUtils.isEmpty(text) ) {
	               Settings.System.putString(contentResolver, mSettingNames[i], null);
	            }
	            else {
	               Settings.System.putString(contentResolver, mSettingNames[i], text);
	            }
	        }
			mIpConfiguration.ipAssignment = IpAssignment.STATIC;
			mIpConfiguration.proxySettings = ProxySettings.STATIC;
	        mIpConfiguration.staticIpConfiguration = mStaticIpConfiguration;	
			mEthernetManager = (EthernetManager) getSystemService(Context.ETHERNET_SERVICE);
			if (mUseStaticIpSwitch.isChecked())
	            mEthernetManager.setConfiguration(mIpConfiguration); 
	        Settings.System.putInt(contentResolver,ETHERNET_USE_STATIC_IP, mUseStaticIpSwitch.isChecked() ? 1 : 0);
	        
	        // disable ethernet
	        boolean enable = Secure.getInt(getContentResolver(), "isEnthernetOn", 1) == 1;
			LOG("notify Secure.ETHERNET_ON changed. enable = " + enable);
	        if(enable) {
	        	LOG("first disable");
	        	Secure.putInt(getContentResolver(), "isEnthernetOn", 0);
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {}   
				LOG("second enable");
	        	Secure.putInt(getContentResolver(), "isEnthernetOn", 1);    	
	        }
	    }
	   
	    @Override
	    public boolean onPreferenceTreeClick(Preference preference) {
	        
	        boolean result = true;     
	        LOG("onPreferenceTreeClick()  chageState = " + chageState);
	        chageState = true;
	
	        return result;
	    }
	
		@Override
	    public boolean onPreferenceChange(Preference preference, Object newValue) {
	        boolean result = true;
	
	        String key = preference.getKey();
	        LOG("onPreferenceChange() : key = " + key);
	
	        if ( null == key ) {
	            return true;
	        }else if (key.equals(KEY_USE_STATIC_IP)) {
	
	
	        }else if ( key.equals(KEY_IP_ADDRESS) 
	                || key.equals(KEY_GATEWAY)
	                || key.equals(KEY_NETMASK)
	                || key.equals(KEY_DNS1)
	                || key.equals(KEY_DNS2) ) { 
	
	            String value = (String) newValue;       
	            
	            LOG("onPreferenceChange() : value = " + value);
	
	            
	            if ( TextUtils.isEmpty(value) ) {
	                
	                ( (EditTextPreference)preference).setText(value);
	                
	                preference.setSummary(value);
	                
	                result = true;
	            }
	            
	            else  if ( !isValidIpAddress(value) ) {
	                LOG("onPreferenceChange() : IP address user inputed is INVALID." );
	                
	                Toast.makeText(getActivity(), R.string.ethernet_ip_settings_invalid_ip, Toast.LENGTH_LONG).show();
	               
	                return false;
	            }
	            
	            else {
	                
	                ( (EditTextPreference)preference).setText(value);
	               
	                preference.setSummary(value);
	                
	                result = true;
	            }
	        }
	        return result;
	    }    
	    
	    private boolean isValidIpAddress(String value) {
	        
	        int start = 0;
	        int end = value.indexOf('.');
	        int numBlocks = 0;
	        
	        while (start < value.length()) {
	            
	            if ( -1 == end ) {
	                end = value.length();
	            }
	
	            try {
	                int block = Integer.parseInt(value.substring(start, end));
	                if ((block > 255) || (block < 0)) {
	                    Log.w(TAG, "isValidIpAddress() : invalid 'block', block = " + block);
	                    return false;
	                }
	            } catch (NumberFormatException e) {
	                Log.w(TAG, "isValidIpAddress() : e = " + e);
	                return false;
	            }
	            
	            numBlocks++;
	            
	            start = end + 1;
	            end = value.indexOf('.', start);
	        }
	        
	        return numBlocks == 4;
	    }
	    
	    private boolean isIpDataInUiComplete() {
	        ContentResolver contentResolver = getContentResolver();
	        for (int i = 0; i < (mPreferenceKeys.length - 1); i++) {
	            EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]);
	            String text = preference.getText();
	            LOG("isIpDataInUiComplete() : text = " + text);
	            if ( null == text || TextUtils.isEmpty(text) ) {
	                return false;
	            }
	        }
	        return true;
	    }
	}

到这一步 Settings 的修改就完成了,就能实现上图的效果了,你可以mm push看效果了

如果你编译报错,大概是 Settings 中没有添加对应的变量,我的本来就有的,

没有的可参考下面的加一下

frameworks\base\core\java\android\provider\Settings.java
 

// Intent actions for Settings
// ethernet
public static final String ETHERNET_STATIC_IP = "ethernet_static_ip";
public static final String ETHERNET_STATIC_GATEWAY = "ethernet_static_gateway";
public static final String ETHERNET_STATIC_NETMASK = "ethernet_static_netmask";
public static final String ETHERNET_STATIC_DNS1 = "ethernet_static_dns1";
public static final String ETHERNET_STATIC_DNS2 = "ethernet_static_dns2";
public static final String IS_ETHERNET_OPEN = "isEthernetOpen";
public static final String IS_ETHERNET_STATUC_OPEN = "isEthernetStaticOpen";

加完后你需要先 make update-api成功后,在重新 mm 编译应该就好了

二、framework 流程分析

驱动大哥已经把驱动都搞定了,现在直接插上网线,设备就能上网,网卡图标也正常显示。我们需要控制网卡的开关,先来简单看下流程。

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetService.java

public final class EthernetService extends SystemService {

    private static final String TAG = "EthernetService";
    final EthernetServiceImpl mImpl;

    public EthernetService(Context context) {
        super(context);
        mImpl = new EthernetServiceImpl(context);
    }

    @Override
    public void onStart() {
        Log.i(TAG, "Registering service " + Context.ETHERNET_SERVICE);
        publishBinderService(Context.ETHERNET_SERVICE, mImpl);
    }

    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            mImpl.start();
        }
    }
}

EthernetService 继承了系统服务,那自然也就是系统服务,如果挂掉会自动重新创建,严重情况会导致系统重启,我就试出来过。看下当 PHASE_SYSTEM_SERVICES_READY 准备完成,调用 EthernetServiceImpl 的 start()

来看下这个 start() 都干了啥

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetServiceImpl.java

    public void start() {
        Log.i(TAG, "Starting Ethernet service");

        HandlerThread handlerThread = new HandlerThread("EthernetServiceThread");
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper());

        mTracker = new EthernetTracker(mContext, mHandler);
		mTracker.start();
        mStarted.set(true);
}

主要创建了 EthernetTracker,这个类是 9.0 中新增出来的,用于监听以太网的切换、以太网判断当前网络是否可用等一系列操作。之前 8.1 中都集成在 EthernetNetworkFactory 中,继续跟进

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetTracker.java

void start() {
        mConfigStore.read();

        // Default interface is just the first one we want to track.
        mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface();
        final ArrayMap<String, IpConfiguration> configs = mConfigStore.getIpConfigurations();
        Log.e(TAG, "mIpConfigForDefaultInterface== " + mIpConfigForDefaultInterface);
        Log.i(TAG, "IpConfiguration size== " + configs.size());
        for (int i = 0; i < configs.size(); i++) {
            mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i));
        }

        try {
            mNMService.registerObserver(new InterfaceObserver());
        } catch (RemoteException e) {
            Log.e(TAG, "Could not register InterfaceObserver " + e);
        }

        mHandler.post(this::trackAvailableInterfaces);
    }

mConfigStore 对象用来管理保存的 IpConfigStore 信息,EthernetConfigStore 中通过读取 /misc/ethernet/ipconfig.txt 中保存的信息进行维护一个 ArrayMap<String, IpConfiguration>,根据打印的日志看,start() 中每次获取到的 size 都为 0,基本上没起作用。值得一提的是,在 EthernetTracker 的构造方法中通过解析 config_ethernet_interfaces 字符串也可向 map 中添加初始信息。

EthernetTracker(Context context, Handler handler) {
        mHandler = handler;

        // The services we use.
        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
        mNMService = INetworkManagementService.Stub.asInterface(b);

        // Interface match regex.
        mIfaceMatch = context.getResources().getString(
                com.android.internal.R.string.config_ethernet_iface_regex);

        // Read default Ethernet interface configuration from resources
        final String[] interfaceConfigs = context.getResources().getStringArray(
                com.android.internal.R.array.config_ethernet_interfaces);
        for (String strConfig : interfaceConfigs) {
            parseEthernetConfig(strConfig);
        }

        mConfigStore = new EthernetConfigStore();

        NetworkCapabilities nc = createNetworkCapabilities(true /* clear default capabilities */);
        mFactory = new EthernetNetworkFactory(handler, context, nc);
        mFactory.register();
    }

private void parseEthernetConfig(String configString) {
        String[] tokens = configString.split(";");
        String name = tokens[0];
        String capabilities = tokens.length > 1 ? tokens[1] : null;
        NetworkCapabilities nc = createNetworkCapabilities(
                !TextUtils.isEmpty(capabilities)  /* clear default capabilities */, capabilities);
        mNetworkCapabilities.put(name, nc);

        if (tokens.length > 2 && !TextUtils.isEmpty(tokens[2])) {
            IpConfiguration ipConfig = parseStaticIpConfiguration(tokens[2]);
            mIpConfigurations.put(name, ipConfig);
        }
    }

config_ethernet_interfaces 的初始值位置在

frameworks\base\core\res\res\values\config.xml

<!-- Regex of wired ethernet ifaces -->
    <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>



    <!-- Configuration of Ethernet interfaces in the following format:
         <interface name|mac address>;[Network Capabilities];[IP config]
         Where
               [Network Capabilities] Optional. A comma seprated list of network capabilities.
                   Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
               [IP config] Optional. If empty or not specified - DHCP will be used, otherwise
                   use the following format to specify static IP configuration:
		       ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
                       domains=<comma-sep-domains> 
         -->
    <string-array translatable="false" name="config_ethernet_interfaces">
        <!--
        <item>eth1;12,13,14,15;ip=192.168.0.10/24 gateway=192.168.0.1 dns=4.4.4.4,8.8.8.8</item>
        <item>eth2;;ip=192.168.0.11/24</item>
        -->
    </string-array>

好了继续回到刚刚的 start() 中,接下来应该调用 trackAvailableInterfaces(),来看下完整的流程,maybeTrackInterface 中先进行 iface 判断,这个指代要使用的网口名称,通过命令 ifconfig -a 可以看到你设备下的所有网口名称。

 mIfaceMatch 对应刚刚的 config.xml 中配置的 eth\d, 所以只有是 eth 打头并且 mFactory 中不存在的才能设置以太网连接,这很关键

继续调用 addInterface(), 将 iface 对应的 ipConfiguration 通过 mFactory addInterface,再然后 updateInterfaceState 广播通知当前以太网连接状态 CONNECTED/CONNECTED
 

private void trackAvailableInterfaces() {
        try {
            final String[] ifaces = mNMService.listInterfaces();
            for (String iface : ifaces) {
                maybeTrackInterface(iface);
            }
        } catch (RemoteException | IllegalStateException e) {
            Log.e(TAG, "Could not get list of interfaces " + e);
        }
    }

private void maybeTrackInterface(String iface) {
        if (DBG) Log.i(TAG, "maybeTrackInterface " + iface);
        // If we don't already track this interface, and if this interface matches
        // our regex, start tracking it.
        if (!iface.matches(mIfaceMatch) || mFactory.hasInterface(iface)) {
            Log.d(TAG, iface + "  return ");
            return;
        }

        Log.e(TAG, "maybeTrackInterface " + iface);
        if (mIpConfigForDefaultInterface != null) {
            updateIpConfiguration(iface, mIpConfigForDefaultInterface);
            mIpConfigForDefaultInterface = null;
        }

        addInterface(iface);
    }

private void addInterface(String iface) {
        InterfaceConfiguration config = null;
        // Bring up the interface so we get link status indications.
        try {
            mNMService.setInterfaceUp(iface);
            config = mNMService.getInterfaceConfig(iface);
        } catch (RemoteException | IllegalStateException e) {
            // Either the system is crashing or the interface has disappeared. Just ignore the
            // error; we haven't modified any state because we only do that if our calls succeed.
            Log.e(TAG, "Error upping interface " + iface, e);
        }

        if (config == null) {
            Log.e(TAG, "Null interface config for " + iface + ". Bailing out.");
            return;
        }

        final String hwAddress = config.getHardwareAddress();

        NetworkCapabilities nc = mNetworkCapabilities.get(iface);
        if (nc == null) {
            // Try to resolve using mac address
            nc = mNetworkCapabilities.get(hwAddress);
            if (nc == null) {
                nc = createDefaultNetworkCapabilities();
            }
        }
        IpConfiguration ipConfiguration = mIpConfigurations.get(iface);
        if (ipConfiguration == null) {
            ipConfiguration = createDefaultIpConfiguration();
        }

        Log.d(TAG, "Started tracking interface " + iface);
        mFactory.addInterface(iface, hwAddress, nc, ipConfiguration);

        // Note: if the interface already has link (e.g., if we crashed and got
        // restarted while it was running), we need to fake a link up notification so we
        // start configuring it.
        if (config.hasFlag("running")) {
            updateInterfaceState(iface, true);
        }
    }

private void updateInterfaceState(String iface, boolean up) {
        Log.e(TAG, "updateInterfaceState up==" + up);
        boolean modified = mFactory.updateInterfaceLinkState(iface, up);
        if (modified) {
            boolean restricted = isRestrictedInterface(iface);
            int n = mListeners.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    if (restricted) {
                        ListenerInfo listenerInfo = (ListenerInfo) mListeners.getBroadcastCookie(i);
                        if (!listenerInfo.canUseRestrictedNetworks) {
                            continue;
                        }
                    }
                    mListeners.getBroadcastItem(i).onAvailabilityChanged(iface, up);
                } catch (RemoteException e) {
                    // Do nothing here.
                }
            }
            mListeners.finishBroadcast();
        }
    }

 知道了 EthernetTracker 的 start() 去连接以太网,那么我们在 EthernetServiceImpl 中增加布尔判断就能控制以太网开关状态。

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetServiceImpl.java
 

public class EthernetServiceImpl extends IEthernetManager.Stub {
    private static final String TAG = "EthernetServiceImpl";

    public static final String IS_ETHERNET_OPEN = Settings.IS_ETHERNET_OPEN;
    public static final String ETHERNET_USE_STATIC_IP = Settings.IS_ETHERNET_STATUC_OPEN; 

    private final Context mContext;
    private final AtomicBoolean mStarted = new AtomicBoolean(false);
    private IpConfiguration mIpConfiguration;
    private final EthernetOpenedObserver mOpenObserver = new EthernetOpenedObserver();
    private final EthernetStaticObserver mStaticObserver = new EthernetStaticObserver(); 

    private Handler mHandler;
    private EthernetTracker mTracker;

    public EthernetServiceImpl(Context context) {
        mContext = context;

        Log.i(TAG, "Creating EthernetConfigStore");
 
        mContext.getContentResolver().registerContentObserver(
            System.getUriFor(IS_ETHERNET_OPEN), false, mOpenObserver);

        mContext.getContentResolver().registerContentObserver(
            System.getUriFor(ETHERNET_USE_STATIC_IP), false, mStaticObserver); 
    }

	....

	public void start() {
        Log.i(TAG, "Starting Ethernet service");

        HandlerThread handlerThread = new HandlerThread("EthernetServiceThread");
        handlerThread.start();
        mHandler = new Handler(handlerThread.getLooper());

        mTracker = new EthernetTracker(mContext, mHandler);

        mIpConfiguration = mTracker.getDefaultIpConfiguration();

        if (getState() == 1) {                 
            if (isStatic()) {
                StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
                staticIpConfiguration.domains = Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_NETMASK);
               
                try {
                    staticIpConfiguration.gateway = InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_GATEWAY));
                    staticIpConfiguration.ipAddress = new LinkAddress(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_IP)), 24);
                    staticIpConfiguration.dnsServers.add(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_DNS1)));
                    staticIpConfiguration.dnsServers.add(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_DNS2)));
                   
                }catch (Exception e){
                        e.printStackTrace();
                }
                mIpConfiguration.ipAssignment = IpAssignment.STATIC;
                mIpConfiguration.proxySettings = ProxySettings.STATIC;
                mIpConfiguration.staticIpConfiguration = staticIpConfiguration;
            }

            mTracker.start();
            mStarted.set(true);
        }  
        
    }

	private boolean isStatic()
    {
        Log.e(TAG, "EthernetServiceImpl isStatic()  " 
            + Settings.System.getInt(mContext.getContentResolver(),ETHERNET_USE_STATIC_IP,0));
     return Settings.System.getInt(mContext.getContentResolver(),ETHERNET_USE_STATIC_IP,0) ==1;
    }   
 

    private int getState()
    {
        int state = Settings.System.getInt(mContext.getContentResolver(), IS_ETHERNET_OPEN,0);
        Log.e(TAG, "EthernetServiceImpl getState()  " + state);
       return state;

    }

	....

	 @Override
    public void setConfiguration(String iface, IpConfiguration config) {
        if (!mStarted.get()) {
            Log.w(TAG, "System isn't ready enough to change ethernet configuration");
        }

        enforceConnectivityInternalPermission();

        if (mTracker.isRestrictedInterface(iface)) {
            enforceUseRestrictedNetworksPermission();
        }

        Log.e(TAG, "setConfiguration iface="+iface);
        // TODO: this does not check proxy settings, gateways, etc.
        // Fix this by making IpConfiguration a complete representation of static configuration.
        mTracker.updateIpConfiguration(iface, new IpConfiguration(config));
        //add
        mTracker.removeInterface(iface);
        mTracker.start();
    }

	....

	private final class EthernetOpenedObserver extends ContentObserver {
        public EthernetOpenedObserver() {
            super(new Handler());
        }

        @Override
        public void onChange(boolean selfChange, Uri uri, int userId) {
          super.onChange(selfChange, uri, userId);
            Log.i(TAG, "EthernetServiceImpl isEthernetOpen onChange....");
            if (getState() == 1) {
                if (isStatic()) {
                    StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
                    staticIpConfiguration.domains = Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_NETMASK);
                   
                    try {
                        staticIpConfiguration.gateway = InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_GATEWAY));
                        staticIpConfiguration.ipAddress = new LinkAddress(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_IP)), 24);
                        staticIpConfiguration.dnsServers.add(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_DNS1)));
                        staticIpConfiguration.dnsServers.add(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_DNS2)));
                       
                        }
                        catch (Exception e){
                            e.printStackTrace();
                        }

                        mIpConfiguration.ipAssignment = IpAssignment.STATIC;
                        mIpConfiguration.proxySettings = ProxySettings.STATIC;
                        mIpConfiguration.staticIpConfiguration = staticIpConfiguration;
                        
                }
                mTracker.start();
                mStarted.set(true);
            }else {    
                mTracker.stop();        
           }
        }
    }

    private final class EthernetStaticObserver extends ContentObserver {
        public EthernetStaticObserver() {
            super(new Handler());
        }

        @Override
        public void onChange(boolean selfChange, Uri uri, int userId) {
            super.onChange(selfChange, uri, userId);
            Log.i(TAG, "EthernetServiceImpl isEthernetStaticOpen onChange....");
            if (!isStatic()) {
                Log.e(TAG, " no static stop and start");
                mTracker.recoverDHCPIpConfiguration();
                mTracker.stop();
                mTracker.start();           
                mStarted.set(true);
           }  
        }
     }

根据 settings 中设置的值 IS_ETHERNET_OPEN 和 ETHERNET_USE_STATIC_IP 判断是否加载,在 EthernetTracker 中新增如下几个方法

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetTracker.java
 

//关闭网卡,先更新状态为 false, 再从 mFactory 中移除 eth0, 不然关闭后无法再次打开,因为上面提到的判断
public void stop() {
    Log.d(TAG, "EthernetTracker stop ethernet...");
    updateInterfaceState("eth0", false);
    android.os.SystemClock.sleep(200);
    removeInterface("eth0");
 }

//获取默认的 IpConfiguration,如果不存在则新建一个 DHCP 类型的,根据实际情况修改 ipAssignment 和 proxySettings
public IpConfiguration getDefaultIpConfiguration(){
    IpConfiguration ipConfiguration = mIpConfigurations.get("eth0");
    return ipConfiguration != null ? ipConfiguration : 
        new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null);
}

//从静态 IP 切换
public void recoverDHCPIpConfiguration(){
    mIpConfigurations.put("eth0", new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null));
}

测试发现频繁点击 静态IP 开关时,出现了数组角标越界的情况,应该是 add 和 remove iface导致的,直接将打印 try 一下就可以。

2019-10-21 17:01:38.675 1075-1285/? E/AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: EthernetServiceThread
    java.lang.ArrayIndexOutOfBoundsException: length=2; index=-1
        at com.android.internal.util.StateMachine$SmHandler.getCurrentState(StateMachine.java:1151)
        at com.android.internal.util.StateMachine$SmHandler.access$1300(StateMachine.java:681)
        at com.android.internal.util.StateMachine.toString(StateMachine.java:2088)
        at java.lang.String.valueOf(String.java:2896)
        at java.lang.StringBuilder.append(StringBuilder.java:132)
        at com.android.server.ethernet.EthernetNetworkFactory$NetworkInterfaceState.toString(EthernetNetworkFactory.java:422)
        at java.lang.String.valueOf(String.java:2896)
        at java.lang.StringBuilder.append(StringBuilder.java:132)
        at com.android.server.ethernet.EthernetNetworkFactory.networkForRequest(EthernetNetworkFactory.java:213)
        at com.android.server.ethernet.EthernetNetworkFactory.acceptRequest(EthernetNetworkFactory.java:78)
        at android.net.NetworkFactory.evalRequest(NetworkFactory.java:234)
        at android.net.NetworkFactory.evalRequests(NetworkFactory.java:253)
        at android.net.NetworkFactory.handleSetFilter(NetworkFactory.java:204)
        at android.net.NetworkFactory.handleMessage(NetworkFactory.java:149)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.os.HandlerThread.run(HandlerThread.java:65)

这样就能实现开头的动图效果了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1301238.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode热题100】【滑动窗口】找到字符串中所有字母异位词

给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 示例 1: 输入: s "cbaebabacd", p "…

Java Web——过滤器 监听器

目录 1. Filter & 过滤器 1.1. 过滤器概述 1.2. 过滤器的使用 1.3. 过滤器生命周期 1.4. 过滤器链的使用 1.5. 注解方式配置过滤器 2. Listener & 监听器 2.1. 监听器概述 2.2. Java Web的监听器 2.2.1. 常用监听器 2.2.1.1. ServletContextListener监听器 …

深度学习与逻辑回归模型的融合--TensorFlow多元分类的高级应用

手写数字识别 文章目录 手写数字识别1、线性回归VS逻辑回归Sigmoid函数 2、逻辑回归的基本模型-神经网络模型3、多元分类基本模型4、TensorFlow实战解决手写数字识别问题准备数据集数据集划分 特征数据归一化归一化方法归一化场景 标签数据独热编码One-Hot编码构建模型损失函数…

RT-DERT改进策略:AKConv即插即用,轻松涨点

摘要 提出了一种算法&#xff0c;用于生成任意尺寸卷积核的初始采样坐标。与常规卷积核相比&#xff0c;提出的AKConv实现了不规则卷积核的函数来提取特征&#xff0c;为各种变化目标提供具有任意采样形状和尺寸的卷积核&#xff0c;弥补了常规卷积的不足。在COCO2017和VisDro…

网络设备的健康检查方式

网络设备的健康检查方式 L3检查 通过ICMP来检查IP地址是否正常 L4检查 通过三次握手来检查端口号是否正常 L7检查 通过真实的应用通信来检查应用程序是否正常

实战-docker方式部署个人私有云相册-PhotoPrism-2023.12.10-测试成功

实战-docker方式部署个人私有云相册-PhotoPrism-2023.12.10-测试成功 目录 文章目录 实战-docker方式部署个人私有云相册-PhotoPrism-2023.12.10-测试成功目录需求前提环境环境1、部署2、测试3、使用4、效果总结参考关于我最后 需求 目前为止&#xff1a; 自己的博客、知识库…

【分布式】浅谈分布式事务及解决方案

目录 一、背景 1.1、本地事务的基本概念 1.2、本地事务的基本特性 1.3、为什么需要分布式事务&#xff1f; 二、分布式事务常见解决方案 2.1、两阶段提交&#xff08;2PC&#xff09; 2.1.1、2PC实现原理 准备阶段&#xff08;Prepare phase&#xff09; 提交阶段&…

Oracle-pl/sql developer客户端连接报错问题分析

问题一&#xff1a; 用户在windows电脑使用pl/sql developer客户端使用tns方式连接数据库时&#xff0c;出现ORA-12170 TNS连接超时报错 使用ezconnect方式连接可以成功 问题一分析: 首先&#xff0c;查看pl/sql developer软件的Oracle客户端配置configure-->preferences,确…

详细介绍下OP-TEE,以及TF-A与OP-TEE的关系

什么是OP-TEE OP-TEE&#xff08;Open Portable Trusted Execution Environment&#xff09;是一个开源的可信执行环境&#xff08;TEE&#xff09;框架&#xff0c;用于嵌入式系统中的安全应用程序执行。它提供了一种安全的执行环境&#xff0c;用于保护敏感数据和执行安全操…

JRT文件服务实现

网站与客户端打印和导出方面已经无大碍了&#xff0c;今天抽时间整整文件服务&#xff0c;文件服务设计可以查看下面连接。原理一样&#xff0c;代码会有些变化。 文件服务设计 首先实现文件服务的服务端&#xff0c;就是一个业务脚本&#xff0c;用来接收上传、移动和删除文件…

大华摄像头windows、linuxJavaSDK开发使用

文章目录 简介环境要求库加载问题及解决方法大华摄像头Java SDK&#xff0c;完成摄像头设备登录、视频录像目录结构windows 的c代码Linux的C代码项目结构 登录云台控制录像调用的接口注意码云地址 简介 本文档主要介绍 SDK 接口参考信息&#xff0c;包括主要功能、接口函数和回…

使用Git进行版本控制

参考&#xff1a;《Python编程从入门到实践》 前言1、安装、配置 Git1.1 在Linux系统中安装Git1.2 在OS X系统中安装Git1.3 在Windows系统中安装Git1.4 配置Git 2、创建项目3、忽略文件4、初始化仓库5、检查状态6、将文件加入到仓库中7、执行提交8、查看提交历史 前言 版本控制…

计算机毕业设计 SpringBoot的乐乐农产品销售系统 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

stu05-前端的几种常用开发工具

前端的开发工具有很多&#xff0c;可以说有几十种&#xff0c;包括记事本都可以作为前端的开发工具。下面推荐的是常用的几种前端开发工具。 1.DCloud HBuilder&#xff08;轻量级&#xff09; HBuilder是DCloud&#xff08;数字天堂&#xff09;推出的一款支持HTML5的web开发…

HCIP---RSTP/MSTP

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 STP协议虽然能够解决环路问题&#xff0c;但是收敛速度慢&#xff0c;影响了用户通信质量。IEEE于2001年发布的802.1w标准定义了快速生成树协议RSTP&#xff08;Rapid Spanning-Tree Proto…

309. 买卖股票的最佳时机含冷冻期(leetcode) 动态规划思想

文章目录 前言一、题目分析二、算法原理1.状态表示2.状态转移方程3.初始化边界条件4.填表顺序5.返回值是什么 三、代码实现总结 前言 在本文章中&#xff0c;我们将要详细介绍一下Leetcode中买卖股票的最佳时机含冷冻期相关的内容&#xff0c;本题采用动态规划的思想解决 一、…

Android Studio Gradle下载慢解决方法

Android Studio Gradle下载慢解决方法 最近在练习模型部署&#xff0c;主要是在手机端部署&#xff0c;所以使用到了Android Studio&#xff0c;但是在创建项目的时候&#xff0c;一致在下载gradle&#xff0c;而且网速还很慢&#xff0c;不对&#xff0c;是极慢哪种&#xff0…

研表究明,文字的序顺并不定一能响影GPT-4读阅

深度学习自然语言处理 原创作者&#xff1a;yy 很多年前&#xff0c;你一定在互联网上看过这张图&#xff0c;展示了人脑能够阅读和理解打乱顺序的单词和句子&#xff01;而最近东京大学的研究发现&#xff0c;大语言模型&#xff08;LLMs&#xff09; 尤其是 GPT-4&#xff0c…

【设计模式--创建型--原型模式】

设计模式--创建型--原型模式 原型模式概述结构实现结果 案例代码结果使用场景 扩展&#xff08;深\浅克隆&#xff09;浅克隆演示&#xff1a;结果&#xff1a;使用深克隆&#xff08;利用对象流&#xff09;结果 原型模式 概述 用一个已经创建的实例作为原型&#xff0c;通过…

【MySQL】MySQL库的操作

MySQL库的操作 一、创建数据库创建数据库案例字符集和校验规则校验规则对数据库的影响 二、操纵数据库1、查看数据库2、查看当前正在使用的数据库3、使用数据库4、显示创建语句5、数据库删除6、数据库的修改7、备份和恢复8、查看连接情况 一、创建数据库 创建数据库的语法如下…