USB开启ADB设置流程

第一步:设置里打开adb调试选项

源码路径:packages/apps/Settings/src/com/android/settings/development/AdbPreferenceController.java

java 复制代码
public void onAdbDialogConfirmed() {
        writeAdbSetting(true);
}

writeAdbSetting 函数所在源码路径:frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java

java 复制代码
protected void writeAdbSetting(boolean enabled) {
    Settings.Global.putInt(mContext.getContentResolver(),
            Settings.Global.ADB_ENABLED, enabled ? ADB_SETTING_ON : ADB_SETTING_OFF);
    notifyStateChanged();
}
 
private void notifyStateChanged() {
    LocalBroadcastManager.getInstance(mContext)
            .sendBroadcast(new Intent(ACTION_ENABLE_ADB_STATE_CHANGED));
}

上述代码主要做了两个操作:

1)修改 Settings的 adb_enable 值

2)广播adb状态改变

setting应用中有监听adb状态广播:packages/apps/Settings/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java

其作用只是调用其注册的Controller的onAdbSettingChanged方法。

java 复制代码
private final BroadcastReceiver mEnableAdbReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        for (AbstractPreferenceController controller : mPreferenceControllers) {
            if (controller instanceof AdbOnChangeListener) {
                ((AdbOnChangeListener) controller).onAdbSettingChanged();
            }
        }
    }
};   
private void registerReceivers() {
    LocalBroadcastManager.getInstance(getContext())
            .registerReceiver(mEnableAdbReceiver, new IntentFilter(
                    AdbPreferenceController.ACTION_ENABLE_ADB_STATE_CHANGED));
    final IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
    getActivity().registerReceiver(mBluetoothA2dpReceiver, filter);
}

setting app 中 接口同目录的 VerifyAppsOverUsbPreferenceController类 继承了 AdbOnChangeListener 接口

java 复制代码
@Override
public void updateState(Preference preference) {
    final RestrictedSwitchPreference restrictedPreference =
        (RestrictedSwitchPreference) preference;
    if (!shouldBeEnabled()) {
        restrictedPreference.setChecked(false);
        restrictedPreference.setDisabledByAdmin(null);
        restrictedPreference.setEnabled(false);
        return;
    }
 
 
    final EnforcedAdmin enforcingAdmin = mRestrictedLockUtils.checkIfRestrictionEnforced(
            mContext, UserManager.ENSURE_VERIFY_APPS, UserHandle.myUserId());
    if (enforcingAdmin != null) {
        restrictedPreference.setChecked(true);
        restrictedPreference.setDisabledByAdmin(enforcingAdmin);
        return;
    }
 
    restrictedPreference.setEnabled(true);
    final boolean checked = Settings.Global.getInt(mContext.getContentResolver(),
            Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, SETTING_VALUE_ON)
            != SETTING_VALUE_OFF;
    restrictedPreference.setChecked(checked);
}
 
@Override
public void onAdbSettingChanged() {
    if (isAvailable()) {
        updateState(mPreference);
    }
}

上述代码只是做一些状态的改变。

第二步:开启ADB服务连接

修改settings里adb_enable的值时,会触发相关的监听。AdbService初始化时,监听了adb_enable 状态改变

java 复制代码
private void initAdbState() {
    try {
        /*
         * Use the normal bootmode persistent prop to maintain state of adb across
         * all boot modes.
         */
        mIsAdbUsbEnabled = true;//containsFunction(
                //SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
               // UsbManager.USB_FUNCTION_ADB);
         
        mIsAdbWifiEnabled = "1".equals(
                SystemProperties.get(WIFI_PERSISTENT_CONFIG_PROPERTY, "0"));
 
 
        // register observer to listen for settings changes
        mObserver = new AdbSettingsObserver();
        mContentResolver.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),  //监听 adb_enable值的改变
                false, mObserver);
        mContentResolver.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED),
                false, mObserver);
    } catch (Exception e) {
        Slog.e(TAG, "Error in initAdbState", e);
    }
}
 
 
 
private class AdbSettingsObserver extends ContentObserver {
    private final Uri mAdbUsbUri = Settings.Global.getUriFor(Settings.Global.ADB_ENABLED);
    private final Uri mAdbWifiUri = Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED);
 
 
    AdbSettingsObserver() {
        super(null);
    }
 
    @Override
    public void onChange(boolean selfChange, @NonNull Uri uri, @UserIdInt int userId) {
        if (mAdbUsbUri.equals(uri)) {
            boolean shouldEnable = true;//(Settings.Global.getInt(mContentResolver,   //always enable usb adb
            //        Settings.Global.ADB_ENABLED, 0) > 0);
            FgThread.getHandler().sendMessage(obtainMessage(
                    AdbService::setAdbEnabled, AdbService.this, shouldEnable,
                        AdbTransportType.USB));
        } else if (mAdbWifiUri.equals(uri)) {
            boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
                    Settings.Global.ADB_WIFI_ENABLED, 0) > 0);
            FgThread.getHandler().sendMessage(obtainMessage(
                    AdbService::setAdbEnabled, AdbService.this, shouldEnable,
                        AdbTransportType.WIFI));
        }
    }
}

最终调用 setAdbEnabled 来设置adb 状态,代码如下:

java 复制代码
private void startAdbd() {
    SystemProperties.set(CTL_START, ADBD);
}
 
private void stopAdbd() {
    if (!mIsAdbUsbEnabled && !mIsAdbWifiEnabled) {
        SystemProperties.set(CTL_STOP, ADBD);
    }
}
 
private void setAdbEnabled(boolean enable, byte transportType) {
    if (DEBUG) {
        Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
                + ", mIsAdbWifiEnabled=" + mIsAdbWifiEnabled + ", transportType="
                    + transportType);
    }
 
 
    if (transportType == AdbTransportType.USB && enable != mIsAdbUsbEnabled) {
        mIsAdbUsbEnabled = enable;
    } else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
        mIsAdbWifiEnabled = enable;
        if (mIsAdbWifiEnabled) {
            if (!AdbProperties.secure().orElse(false) && mDebuggingManager == null) {
                // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
                SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
                mConnectionPortPoller =
                        new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
                mConnectionPortPoller.start();
            }
        } else {
            // Stop adb over WiFi.
            SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
            if (mConnectionPortPoller != null) {
                mConnectionPortPoller.cancelAndWait();
                mConnectionPortPoller = null;
            }
        }
    } else {
        // No change
        return;
    }
 
    if (enable) {
        startAdbd();
    } else {
        stopAdbd();
    }
 
    for (IAdbTransport transport : mTransports.values()) {
        try {
            transport.onAdbEnabled(enable, transportType);
        } catch (RemoteException e) {
            Slog.w(TAG, "Unable to send onAdbEnabled to transport " + transport.toString());
        }
    }
 
    if (mDebuggingManager != null) {
        mDebuggingManager.setAdbEnabled(enable, transportType);
    }
}

这里主要包括三步:

1)startAdbd 会设置 ctl.start 属性开启native层的adbd服务

2)调用transport.onAdbEnabled, 代码位于 UsbDeviceManager类

3)调用 mDebuggingManager.setAdbEnabled

UsbDeviceManager类路径位于 frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java

java 复制代码
private static class AdbTransport extends IAdbTransport.Stub {
    private final UsbHandler mHandler;
 
    AdbTransport(UsbHandler handler) {
        mHandler = handler;
    }
 
    @Override
    public void onAdbEnabled(boolean enabled, byte transportType) {
        if (transportType == AdbTransportType.USB) {
            mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
        }
    }
}
@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        ..................
        case MSG_ENABLE_ADB:
            setAdbEnabled(true); // alway true msg.arg1 == 1
            break;
        ..................
    }
}

通过消息发送和处理后执行 setAdbEnabled 方法。

java 复制代码
private void setAdbEnabled(boolean enable) {
    if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
    if (enable) {
        setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_ADB);
    } else {
        setSystemProperty(USB_PERSISTENT_CONFIG_PROPERTY, "");
    }
    setEnabledFunctions(mCurrentFunctions, true);
    updateAdbNotification(false);
}

这个主要做的是设置系统属性 persist.sys.usb.config = adb,然后调用 setEnabledFunctions 函数

java 复制代码
@Override
protected void setEnabledFunctions(long usbFunctions, boolean forceRestart) {
    boolean usbDataUnlocked = isUsbDataTransferActive(usbFunctions);
    if (DEBUG) {
        Slog.d(TAG, "setEnabledFunctions functions=" + usbFunctions + ", "
                + "forceRestart=" + forceRestart + ", usbDataUnlocked=" + usbDataUnlocked);
    }
 
 
    if (usbDataUnlocked != mUsbDataUnlocked) {
        mUsbDataUnlocked = usbDataUnlocked;
        updateUsbNotification(false);
        forceRestart = true;
    }
 
    /**
     * Try to set the enabled functions.
     */
    final long oldFunctions = mCurrentFunctions;
    final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
    if (trySetEnabledFunctions(usbFunctions, forceRestart)) {
        return;
    }
 
    /**
     * Didn't work.  Try to revert changes.
     * We always reapply the policy in case certain constraints changed such as
     * user restrictions independently of any other new functions we were
     * trying to activate.
     */
    if (oldFunctionsApplied && oldFunctions != usbFunctions) {
        Slog.e(TAG, "Failsafe 1: Restoring previous USB functions.");
        if (trySetEnabledFunctions(oldFunctions, false)) {
            return;
        }
    }
 
    /**
     * Still didn't work.  Try to restore the default functions.
     */
    Slog.e(TAG, "Failsafe 2: Restoring default USB functions.");
    if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
        return;
    }
 
    /**
     * Now we're desperate.  Ignore the default functions.
     * Try to get ADB working if enabled.
     */
    Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled).");
    if (trySetEnabledFunctions(UsbManager.FUNCTION_NONE, false)) {
        return;
    }
 
    /**
     * Ouch.
     */
    Slog.e(TAG, "Unable to set any USB functions!");
}

setEnabledFunctions 函数主要调用 trySetEnabledFunctions 设置 usb的状态,如果设置返回失败,则一步步还原设置usb状态

java 复制代码
private boolean trySetEnabledFunctions(long usbFunctions, boolean forceRestart) {
    String functions = null;
    if (usbFunctions != UsbManager.FUNCTION_NONE) {
        functions = UsbManager.usbFunctionsToString(usbFunctions);
    }
    mCurrentFunctions = usbFunctions;
    if (functions == null || applyAdbFunction(functions)
            .equals(UsbManager.USB_FUNCTION_NONE)) {
        functions = UsbManager.usbFunctionsToString(getChargingFunctions());
    }
    functions = applyAdbFunction(functions);
 
 
    String oemFunctions = applyOemOverrideFunction(functions);
 
    if (!isNormalBoot() && !mCurrentFunctionsStr.equals(functions)) {
        setSystemProperty(getPersistProp(true), functions);
    }
 
    if ((!functions.equals(oemFunctions)
            && !mCurrentOemFunctions.equals(oemFunctions))
            || !mCurrentFunctionsStr.equals(functions)
            || !mCurrentFunctionsApplied
            || forceRestart) {
        Slog.i(TAG, "Setting USB config to " + functions);
        mCurrentFunctionsStr = functions;
        mCurrentOemFunctions = oemFunctions;
        mCurrentFunctionsApplied = false;
 
        /**
         * Kick the USB stack to close existing connections.
         */
        setUsbConfig(UsbManager.USB_FUNCTION_NONE);
 
        if (!waitForState(UsbManager.USB_FUNCTION_NONE)) {
            Slog.e(TAG, "Failed to kick USB config");
            return false;
        }
 
        /**
         * Set the new USB configuration.
         */
        setUsbConfig(oemFunctions);
 
        if (mBootCompleted
                && (containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
                || containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
            /**
             * Start up dependent services.
             */
            updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
        }
 
        if (!waitForState(oemFunctions)) {
            Slog.e(TAG, "Failed to switch USB config to " + functions);
            return false;
        }
 
        mCurrentFunctionsApplied = true;
    }
    return true;
}

这里主要包含两步:

1)调用 setUsbConfig 设置usb状态

2)调用 waitForState 判断状态是否设置成功

java 复制代码
private void setUsbConfig(String config) {
    if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
    /**
     * set the new configuration
     * we always set it due to b/23631400, where adbd was getting killed
     * and not restarted due to property timeouts on some devices
     */
    setSystemProperty(USB_CONFIG_PROPERTY, config);
}

设置 sys.usb.config = adb

第二步的waitForState 方法如下:

java 复制代码
private boolean waitForState(String state) {
    // wait for the transition to complete.
    // give up after 1 second.
             
    String value = null;
    for (int i = 0; i < 20; i++) {
        // State transition is done when sys.usb.state is set to the new configuration
        value = getSystemProperty(USB_STATE_PROPERTY, "");
        if (state.equals(value)) return true;
        SystemClock.sleep(50);
    }
    Slog.e(TAG, "waitForState(" + state + ") FAILED: got " + value);
    return false;
}

这个函数在 1s 内循环读取 sys.usb.state 的值,如果最后不是预期的值,则返回false。

最后是执行 mDebuggingManager.setAdbEnabled

java 复制代码
public void setAdbEnabled(boolean enabled, byte transportType) {
    if (transportType == AdbTransportType.USB) {
        mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
                                          : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
    } else if (transportType == AdbTransportType.WIFI) {
        mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MSG_ADBDWIFI_ENABLE
                                          : AdbDebuggingHandler.MSG_ADBDWIFI_DISABLE);
    } else {
        throw new IllegalArgumentException(
                "setAdbEnabled called with unimplemented transport type=" + transportType);
    }
}

对应消息处理函数:

java 复制代码
public void handleMessage(Message msg) {
 
    if (mAdbKeyStore == null) {
        mAdbKeyStore = new AdbKeyStore();
    }
    switch (msg.what) {
        case MESSAGE_ADB_ENABLED:
           if (mAdbUsbEnabled) {
               break;
            }
            startAdbDebuggingThread();
            mAdbUsbEnabled = true;
            break;
        case MESSAGE_ADB_DISABLED:
            if (!mAdbUsbEnabled) {
                break;
            }
            stopAdbDebuggingThread();
            mAdbUsbEnabled = false;
            break;
            ....................................
    }
}
 
 
        private void startAdbDebuggingThread() {
            ++mAdbEnabledRefCount;
            if (DEBUG) Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount);
            if (mAdbEnabledRefCount > 1) {
                return;
            }
 
 
            registerForAuthTimeChanges();
            mThread = new AdbDebuggingThread();
            mThread.start();
 
            mAdbKeyStore.updateKeyStore();
            scheduleJobToUpdateAdbKeyStore();
        }
复制代码
startAdbDebuggingThread 开启 java层 adb线程开始接收处理控制命令。
第三步:native层服务及属性

上述服务完成后,java层的相关操作都已经完成,总结来看,java层做了三个操作

1、设置 ctl.start = adb 来打开adb服务。native层 adbd服务入口函数位于 system/core/adb/daemon/main.cpp 文件。

2、设置属性 persist.sys.usb.config = adb

3、设置属性 sys.usb.config = adb

java函数 waitForState 会读取 sys.usb.state 属性的值,如果不是 adb , 会返回false, 接着 setEnabledFunctions 会执行后续的代码将usb状态设置为空。那么 sys.usb.state 属性的值是在哪设置的呢?

在init进程中会监听各种属性的变化,并作出相应的处理,与adb设置相关的init主要有两个:

system/core/rootdir/init.usb.rc

system/core/rootdir/init.usb.configfs.rc

相关的操作中,会将 sys.usb.state 的值设置成 sys.usb.config 的值

bash 复制代码
on property:init.svc.adbd=stopped
    # setprop sys.usb.ffs.ready 0
 
on property:sys.usb.config=adb && property:sys.usb.configfs=1
    start adbd
 
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=adb && property:sys.usb.configfs=1
    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
    setprop sys.usb.state ${sys.usb.config}
 
on property:sys.usb.config=mtp && property:sys.usb.configfs=1
    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
    symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
    setprop sys.usb.state ${sys.usb.config}
 
on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
    start adbd
 
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
    symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
    setprop sys.usb.state ${sys.usb.config}
 
on property:sys.usb.config=ptp && property:sys.usb.configfs=1
    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
    symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
    setprop sys.usb.state ${sys.usb.config}
 
on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
    start adbd
第四步:开机后如何保持adb自动开启

SystemServer 启动时,会调用 startOtherServices 启动 AdbService

java 复制代码
private static final String ADB_SERVICE_CLASS ="com.android.server.adb.AdbService$Lifecycle";
 
 
// Start ADB Debugging Service
t.traceBegin("StartAdbService");
try {
    mSystemServiceManager.startService(ADB_SERVICE_CLASS);
} catch (Throwable e) {
    Slog.e(TAG, "Failure starting AdbService");
}
t.traceEnd();

AdbService 启动代码如下:

java 复制代码
public static class Lifecycle extends SystemService {
    private AdbService mAdbService;
 
 
    public Lifecycle(Context context) {
        super(context);
    }
 
    @Override
    public void onStart() {
        mAdbService = new AdbService(getContext());
        publishBinderService(Context.ADB_SERVICE, mAdbService);
    }
 
    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
            mAdbService.systemReady();
        } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
            FgThread.getHandler().sendMessage(obtainMessage(
                    AdbService::bootCompleted, mAdbService));
        }
    }
}

创建 AdbService 服务对象并绑定到系统服务中,并在 onBootPhase 回调中处理 systemReady 和 bootCompleted 事件。

java 复制代码
/**
 * Called in response to {@code SystemService.PHASE_ACTIVITY_MANAGER_READY} from {@code
 * SystemServer}.
 */
public void systemReady() {
    if (DEBUG) Slog.d(TAG, "systemReady");
    /*
     * Use the normal bootmode persistent prop to maintain state of adb across
     * all boot modes.
     */
    mIsAdbUsbEnabled = containsFunction(
            SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),UsbManager.USB_FUNCTION_ADB);
    boolean shouldEnableAdbUsb = mIsAdbUsbEnabled || SystemProperties.getBoolean(TestHarnessModeService.TEST_HARNESS_MODE_PROPERTY, false);
    mIsAdbWifiEnabled = "1".equals(SystemProperties.get(WIFI_PERSISTENT_CONFIG_PROPERTY, "0"));
    // make sure the ADB_ENABLED setting value matches the current state
    try {
        Settings.Global.putInt(mContentResolver,
                Settings.Global.ADB_ENABLED, shouldEnableAdbUsb ? 1 : 0);
        Settings.Global.putInt(mContentResolver,
                Settings.Global.ADB_WIFI_ENABLED, mIsAdbWifiEnabled ? 1 : 0);
    } catch (SecurityException e) {
        // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
        Slog.d(TAG, "ADB_ENABLED is restricted.");
    }
}
 
 
/**
 * Called in response to {@code SystemService.PHASE_BOOT_COMPLETED} from {@code SystemServer}.
 */
public void bootCompleted() {
    if (DEBUG) Slog.d(TAG, "boot completed");
    if (mDebuggingManager != null) {
        mDebuggingManager.setAdbEnabled(mIsAdbUsbEnabled, AdbTransportType.USB);
        mDebuggingManager.setAdbEnabled(mIsAdbWifiEnabled, AdbTransportType.WIFI);
    }
}

读取 persist.sys.usb.config 属性值,判断是否包含 adb, 如果包含, 初始化 mIsAdbUsbEnabled = true 。 接着设置adb_enable的值,触发 AdbService 的 AdbSettingsObserver 监听。后续的设置流程与上述分析一致。

相关推荐
nbwenren1 天前
MySQL数据库误删恢复_mysql 数据 误删
数据库·mysql·adb
HUGu RGIN2 天前
MySQL--》如何在MySQL中打造高效优化索引
android·mysql·adb
北冥有鱼被烹2 天前
【微知】rokid glass如何开启无线adb进行APP安装
adb
STER labo3 天前
mysql配置环境变量——(‘mysql‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件解决办法)
数据库·mysql·adb
sjmaysee3 天前
CentOS7安装Mysql5.7(ARM64架构)
adb·架构
AtOR CUES4 天前
MySQL——表操作及查询
android·mysql·adb
mOok ONSC4 天前
mysql9.0windows安装
windows·adb
xxjj998a4 天前
Laravel8.x核心特性详解
数据库·mysql·adb
TeDi TIVE4 天前
Linux下MySQL的简单使用
linux·mysql·adb
TeDi TIVE4 天前
MySQL四种备份表的方式
mysql·adb·oracle