当前位置: 首页 > news >正文

USB偏好设置-Android13

USB偏好设置

  • 1、USB偏好设置界面和入口
  • 2、USB功能设置
    • 2.1 USB功能对应模式
    • 2.2 点击设置
    • 2.3 广播监听刷新
  • 3、日志开关
    • 3.1 Evet日志
    • 3.2 代码中日志开关
    • 3.3 关键日志
  • 4、异常

1、USB偏好设置界面和入口

设置》已连接的设备》USB
packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
packages/apps/Settings/res/xml/usb_details_fragment.xml
在这里插入图片描述

private static List<UsbDetailsController> createControllerList(Context context,UsbBackend usbBackend, UsbDetailsFragment fragment) {List<UsbDetailsController> ret = new ArrayList<>();ret.add(new UsbDetailsHeaderController(context, fragment, usbBackend));ret.add(new UsbDetailsDataRoleController(context, fragment, usbBackend));ret.add(new UsbDetailsFunctionsController(context, fragment, usbBackend));ret.add(new UsbDetailsPowerRoleController(context, fragment, usbBackend));ret.add(new UsbDetailsTranscodeMtpController(context, fragment, usbBackend));return ret;
}

2、USB功能设置

packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java

2.1 USB功能对应模式

FWK模式对应值字符串显示节点
FUNCTION_NONE = 0NONE = 0
FUNCTION_MTP = GadgetFunction.MTPMTP = 1 << 2“文件传输”
FUNCTION_PTP = GadgetFunction.PTPPTP = 1 << 4“PTP”
FUNCTION_RNDIS = GadgetFunction.RNDISRNDIS = 1 << 5“USB 网络共享”
FUNCTION_MIDI = GadgetFunction.MIDIMIDI = 1 << 3“MIDI”
FUNCTION_ACCESSORY = GadgetFunction.ACCESSORYACCESSORY = 1 << 1
FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCEAUDIO_SOURCE = 1 << 6
FUNCTION_ADB = GadgetFunction.ADBADB = 1 << 0“不用于数据传输”

frameworks/base/core/java/android/hardware/usb/UsbManager.java
hardware/interfaces/usb/gadget/1.0/types.hal
frameworks/base/core/proto/android/service/usb.proto

/* Same as android.hardware.usb.gadget.V1_0.GadgetFunction.* */
enum Function {FUNCTION_ADB = 1;FUNCTION_ACCESSORY = 2;FUNCTION_MTP = 4;FUNCTION_MIDI = 8;FUNCTION_PTP = 16;FUNCTION_RNDIS = 32;FUNCTION_AUDIO_SOURCE = 64;
}

2.2 点击设置

mUsbBackend 通过UsbManager.java、UsbService.java、UsbDeviceManager.java设置

packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java

@Override
public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {final long function = UsbBackend.usbFunctionsFromString(preference.getKey());final long previousFunction = mUsbBackend.getCurrentFunctions();if (DEBUG) {Log.d(TAG, "onRadioButtonClicked() function : " + function + ", toString() : "+ UsbManager.usbFunctionsToString(function) + ", previousFunction : "+ previousFunction + ", toString() : "+ UsbManager.usbFunctionsToString(previousFunction));}if (function != previousFunction && !Utils.isMonkeyRunning()&& !isClickEventIgnored(function, previousFunction)) {mPreviousFunction = previousFunction;//Update the UI in advance to make it looks smoothfinal SelectorWithWidgetPreference prevPref =(SelectorWithWidgetPreference) mProfilesContainer.findPreference(UsbBackend.usbFunctionsToString(mPreviousFunction));if (prevPref != null) {prevPref.setChecked(false);preference.setChecked(true);}if (function == UsbManager.FUNCTION_RNDIS || function == UsbManager.FUNCTION_NCM) {// We need to have entitlement check for usb tethering, so use API in// TetheringManager.mTetheringManager.startTethering(TetheringManager.TETHERING_USB, new HandlerExecutor(mHandler),mOnStartTetheringCallback);} else {mUsbBackend.setCurrentFunctions(function);}}
}

frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java

public void setCurrentFunctions(long functions) {if (DEBUG) {Slog.d(TAG, "setCurrentFunctions(" + UsbManager.usbFunctionsToString(functions) + ")");}if (functions == UsbManager.FUNCTION_NONE) {MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_CHARGING);} else if (functions == UsbManager.FUNCTION_MTP) {MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MTP);} else if (functions == UsbManager.FUNCTION_PTP) {MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_PTP);} else if (functions == UsbManager.FUNCTION_MIDI) {MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MIDI);} else if (functions == UsbManager.FUNCTION_RNDIS) {MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_RNDIS);} else if (functions == UsbManager.FUNCTION_ACCESSORY) {MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_ACCESSORY);}mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);}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;}

hardware/interfaces/usb/gadget/1.2/default/UsbGadget.cpp

V1_0::Status UsbGadget::setupFunctions(uint64_t functions,const sp<V1_0::IUsbGadgetCallback>& callback,uint64_t timeout) {bool ffsEnabled = false;int i = 0;if (addGenericAndroidFunctions(&monitorFfs, functions, &ffsEnabled, &i) !=V1_0::Status::SUCCESS)return V1_0::Status::ERROR;if ((functions & V1_2::GadgetFunction::ADB) != 0) {ffsEnabled = true;if (addAdb(&monitorFfs, &i) != V1_0::Status::SUCCESS) return V1_0::Status::ERROR;}// Pull up the gadget right away when there are no ffs functions.if (!ffsEnabled) {if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) return V1_0::Status::ERROR;mCurrentUsbFunctionsApplied = true;if (callback) callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);return V1_0::Status::SUCCESS;}monitorFfs.registerFunctionsAppliedCallback(&currentFunctionsAppliedCallback, this);// Monitors the ffs paths to pull up the gadget when descriptors are written.// Also takes of the pulling up the gadget again if the userspace process// dies and restarts.monitorFfs.startMonitor();if (kDebug) ALOGI("Mainthread in Cv");if (callback) {bool pullup = monitorFfs.waitForPullUp(timeout);Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, pullup ? V1_0::Status::SUCCESS : V1_0::Status::ERROR);if (!ret.isOk()) ALOGE("setCurrentUsbFunctionsCb error %s", ret.description().c_str());}return V1_0::Status::SUCCESS;
}Return<void> UsbGadget::setCurrentUsbFunctions(uint64_t functions,const sp<V1_0::IUsbGadgetCallback>& callback,uint64_t timeout) {std::unique_lock<std::mutex> lk(mLockSetCurrentFunction);mCurrentUsbFunctions = functions;mCurrentUsbFunctionsApplied = false;// Unlink the gadget and stop the monitor if running.V1_0::Status status = tearDownGadget();if (status != V1_0::Status::SUCCESS) {goto error;}ALOGI("Returned from tearDown gadget");// Leave the gadget pulled down to give time for the host to sense disconnect.usleep(kDisconnectWaitUs);if (functions == static_cast<uint64_t>(V1_2::GadgetFunction::NONE)) {if (callback == NULL) return Void();Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);if (!ret.isOk())ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());return Void();}status = validateAndSetVidPid(functions);if (status != V1_0::Status::SUCCESS) {goto error;}status = setupFunctions(functions, callback, timeout);if (status != V1_0::Status::SUCCESS) {goto error;}ALOGI("Usb Gadget setcurrent functions called successfully");return Void();error:ALOGI("Usb Gadget setcurrent functions failed");if (callback == NULL) return Void();Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, status);if (!ret.isOk())ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());return Void();
}

hardware/interfaces/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp

Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,int* functionCount) {if (((functions & GadgetFunction::MTP) != 0)) {*ffsEnabled = true;ALOGI("setCurrentUsbFunctions mtp");if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;if (!monitorFfs->addInotifyFd("/dev/usb-ffs/mtp/")) return Status::ERROR;if (linkFunction("ffs.mtp", (*functionCount)++)) return Status::ERROR;// Add endpoints to be monitored.monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep1");monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep2");monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep3");} else if (((functions & GadgetFunction::PTP) != 0)) {*ffsEnabled = true;ALOGI("setCurrentUsbFunctions ptp");if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;if (!monitorFfs->addInotifyFd("/dev/usb-ffs/ptp/")) return Status::ERROR;if (linkFunction("ffs.ptp", (*functionCount)++)) return Status::ERROR;// Add endpoints to be monitored.monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep1");monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep2");monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep3");}if ((functions & GadgetFunction::MIDI) != 0) {ALOGI("setCurrentUsbFunctions MIDI");if (linkFunction("midi.gs5", (*functionCount)++)) return Status::ERROR;}if ((functions & GadgetFunction::ACCESSORY) != 0) {ALOGI("setCurrentUsbFunctions Accessory");if (linkFunction("accessory.gs2", (*functionCount)++)) return Status::ERROR;}if ((functions & GadgetFunction::AUDIO_SOURCE) != 0) {ALOGI("setCurrentUsbFunctions Audio Source");if (linkFunction("audio_source.gs3", (*functionCount)++)) return Status::ERROR;}if ((functions & GadgetFunction::RNDIS) != 0) {ALOGI("setCurrentUsbFunctions rndis");if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;std::string rndisFunction = GetProperty(kVendorRndisConfig, "");if (rndisFunction != "") {if (linkFunction(rndisFunction.c_str(), (*functionCount)++)) return Status::ERROR;} else {// link gsi.rndis for older pixel projectsif (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;}}if ((functions & GadgetFunction::NCM) != 0) {ALOGI("setCurrentUsbFunctions ncm");if (linkFunction("ncm.gs6", (*functionCount)++)) return Status::ERROR;}return Status::SUCCESS;
}

了解 USB Gadget HAL API 架构
在这里插入图片描述

2.3 广播监听刷新

广播监听刷新 onUsbConnectionChanged > refresh
packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbConnectionBroadcastReceiver.java
packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java
packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java

/*** Interface definition for a callback to be invoked when usb connection is changed.*/
interface UsbConnectionListener {void onUsbConnectionChanged(boolean connected, long functions, int powerRole, int dataRole,boolean isUsbConfigured);
}
private UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener =(connected, functions, powerRole, dataRole, isUsbFigured) -> {for (UsbDetailsController controller : mControllers) {controller.refresh(connected, functions, powerRole, dataRole);}};

3、日志开关

3.1 Evet日志

如:MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_MTP);

frameworks/base/core/java/com/android/internal/logging/MetricsLogger.java
frameworks/base/core/java/android/metrics/LogMaker.java
frameworks/base/proto/src/metrics_constants/metrics_constants.proto

   // The view or control was activated.TYPE_ACTION = 4;// These values should never appear in log outputs - they are reserved for// internal platform metrics use.RESERVED_FOR_LOGBUILDER_CATEGORY = 757;RESERVED_FOR_LOGBUILDER_TYPE = 758;RESERVED_FOR_LOGBUILDER_SUBTYPE = 759;// ACTION: Usb config has been changed to charging// CATEGORY: SETTINGS// OS: PACTION_USB_CONFIG_CHARGING = 1275;// ACTION: Usb config has been changed to mtp (file transfer)// CATEGORY: SETTINGS// OS: PACTION_USB_CONFIG_MTP = 1276;// ACTION: Usb config has been changed to ptp (photo transfer)// CATEGORY: SETTINGS// OS: PACTION_USB_CONFIG_PTP = 1277;// ACTION: Usb config has been changed to rndis (usb tethering)// CATEGORY: SETTINGS// OS: PACTION_USB_CONFIG_RNDIS = 1278;// ACTION: Usb config has been changed to midi// CATEGORY: SETTINGS// OS: PACTION_USB_CONFIG_MIDI = 1279;// ACTION: Usb config has been changed to accessory// CATEGORY: SETTINGS// OS: PACTION_USB_CONFIG_ACCESSORY = 1280;

3.2 代码中日志开关

adb 打开:需要重启Settings进程

adb shell setprop log.tag.UsbFunctionsCtrl D
adb shell setprop log.tag.UsbBroadcastReceiver D

packages/apps/Settings/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java

private static final String TAG = "UsbFunctionsCtrl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

3.3 关键日志

start u|SettingsActivity: Switching to|UsbFunctionsCtrl:|UsbDetailsFragment:|UsbBroadcastReceiver:|UsbDeviceManager:|sysui_multi_action: [757,.*,758,4]

android.hardware.usb.gadget@1.1-service-qti:|libusbconfigfs:

4、异常

symlink失败:"Cannot create symlink %s -> %s errno:%d"

hardware/interfaces/usb/gadget/1.2/default/lib/include/UsbGadgetCommon.h

#define GADGET_PATH "/config/usb_gadget/g1/"
#define PULLUP_PATH GADGET_PATH "UDC"
#define PERSISTENT_BOOT_MODE "ro.bootmode"
#define VENDOR_ID_PATH GADGET_PATH "idVendor"
#define PRODUCT_ID_PATH GADGET_PATH "idProduct"
#define DEVICE_CLASS_PATH GADGET_PATH "bDeviceClass"
#define DEVICE_SUB_CLASS_PATH GADGET_PATH "bDeviceSubClass"
#define DEVICE_PROTOCOL_PATH GADGET_PATH "bDeviceProtocol"
#define DESC_USE_PATH GADGET_PATH "os_desc/use"
#define OS_DESC_PATH GADGET_PATH "os_desc/b.1"
#define CONFIG_PATH GADGET_PATH "configs/b.1/"
#define FUNCTIONS_PATH GADGET_PATH "functions/"
#define FUNCTION_NAME "function"
#define FUNCTION_PATH CONFIG_PATH FUNCTION_NAME
#define RNDIS_PATH FUNCTIONS_PATH "gsi.rndis"

在这里插入图片描述
hardware/interfaces/usb/gadget/1.2/default/lib/UsbGadgetUtils.cpp

int linkFunction(const char* function, int index) {char functionPath[kMaxFilePathLength];char link[kMaxFilePathLength];sprintf(functionPath, "%s%s", FUNCTIONS_PATH, function);sprintf(link, "%s%d", FUNCTION_PATH, index);if (symlink(functionPath, link)) {ALOGE("Cannot create symlink %s -> %s errno:%d", link, functionPath, errno);return -1;}return 0;
}Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,int* functionCount) {if (((functions & GadgetFunction::MTP) != 0)) {*ffsEnabled = true;ALOGI("setCurrentUsbFunctions mtp");if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;if (!monitorFfs->addInotifyFd("/dev/usb-ffs/mtp/")) return Status::ERROR;if (linkFunction("ffs.mtp", (*functionCount)++)) return Status::ERROR;// Add endpoints to be monitored.monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep1");monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep2");monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep3");} else if (((functions & GadgetFunction::PTP) != 0)) {*ffsEnabled = true;ALOGI("setCurrentUsbFunctions ptp");if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;if (!monitorFfs->addInotifyFd("/dev/usb-ffs/ptp/")) return Status::ERROR;if (linkFunction("ffs.ptp", (*functionCount)++)) return Status::ERROR;// Add endpoints to be monitored.monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep1");monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep2");monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep3");}if ((functions & GadgetFunction::MIDI) != 0) {ALOGI("setCurrentUsbFunctions MIDI");if (linkFunction("midi.gs5", (*functionCount)++)) return Status::ERROR;}if ((functions & GadgetFunction::ACCESSORY) != 0) {ALOGI("setCurrentUsbFunctions Accessory");if (linkFunction("accessory.gs2", (*functionCount)++)) return Status::ERROR;}if ((functions & GadgetFunction::AUDIO_SOURCE) != 0) {ALOGI("setCurrentUsbFunctions Audio Source");if (linkFunction("audio_source.gs3", (*functionCount)++)) return Status::ERROR;}if ((functions & GadgetFunction::RNDIS) != 0) {ALOGI("setCurrentUsbFunctions rndis");if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;std::string rndisFunction = GetProperty(kVendorRndisConfig, "");if (rndisFunction != "") {if (linkFunction(rndisFunction.c_str(), (*functionCount)++)) return Status::ERROR;} else {// link gsi.rndis for older pixel projectsif (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;}}if ((functions & GadgetFunction::NCM) != 0) {ALOGI("setCurrentUsbFunctions ncm");if (linkFunction("ncm.gs6", (*functionCount)++)) return Status::ERROR;}return Status::SUCCESS;
}

相关文章:

  • 使用Go语言抓取酒店价格数据的技术实现
  • 人工智能模型转ONNX 连接摄像头使用ONNX格式的模型进行推理
  • 前端自动检查更新,适用Vue任何版本项目,包服务端更后客户端更新
  • 工作学习记录
  • UE5数字孪生制作-数据篇(二) - 数据处理
  • WGCLOUD实践 - wgToken怎么使用
  • nacos做服务配置和服务器发现
  • [LeetCode]-225. 用队列实现栈
  • 前端缓存机制——强缓存、弱缓存、启发式缓存
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • 前端Vue 页面滑动监听 拿到滑动的坐标值
  • 移除元素(双指针)
  • 目标检测回归损失函数(看情况补...)
  • 接收表单数据
  • HTTP 协议详解-上(Fiddler 抓包演示)
  • 【前端学习】-粗谈选择器
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • co模块的前端实现
  • ERLANG 网工修炼笔记 ---- UDP
  • Java IO学习笔记一
  • mongo索引构建
  • Python中eval与exec的使用及区别
  • tweak 支持第三方库
  • yii2权限控制rbac之rule详细讲解
  • 机器学习学习笔记一
  • 实战|智能家居行业移动应用性能分析
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • ​HTTP与HTTPS:网络通信的安全卫士
  • ​io --- 处理流的核心工具​
  • #HarmonyOS:基础语法
  • #控制台大学课堂点名问题_课堂随机点名
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (2020)Java后端开发----(面试题和笔试题)
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (二)hibernate配置管理
  • (二)Linux——Linux常用指令
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (万字长文)Spring的核心知识尽揽其中
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .bat批处理(六):替换字符串中匹配的子串
  • .NET开源快速、强大、免费的电子表格组件
  • .Net中的集合
  • .vue文件怎么使用_vue调试工具vue-devtools的安装
  • @RestController注解的使用
  • @TableLogic注解说明,以及对增删改查的影响
  • [ IO.File ] FileSystemWatcher
  • [ vulhub漏洞复现篇 ] ThinkPHP 5.0.23-Rce
  • [ 隧道技术 ] 反弹shell的集中常见方式(四)python反弹shell
  • [2013AAA]On a fractional nonlinear hyperbolic equation arising from relative theory
  • [Android Pro] AndroidX重构和映射