Android中native_handle private_handle_t ANativeWindowBuffer ANativeWindow GraphicBuffer Surface的关系

部署运行你感兴趣的模型镜像

    在阅读SurfaceFlinger HardwareComposer以及gralloc相关代码的过程中,我们经常会遇到native_handle private_handle_t ANativeWindowBuffer ANativeWindow GraphicBuffer Surface等等一系列和memory相关的struct和class,他们相互之间到底是什么区别,又有什么联系呢?本文从struct/class的结构角度分析下上述类型之间的关联.

    概括来说,native_handle private_handle_t ANativeWindowBuffer GraphicBuffer这四个struct/class所描述的是一块memory,而ANativeWindow 和Surface所描述的是一系列上述memeory的组合和对buffer的操作方法.有的struct/class在比较低的level使用,和平台相关,而另外一些在比较高的level使用,和平台无关,还有一些介于低/高level之间,用以消除平台相关性,让android可以方便的运行在不同的平台上.

    我们依次来看下上述struct/class的定义:

1. native_handle 

system/core/include/cutils/native_handle.h

typedef struct native_handle
{
    int version;        /* sizeof(native_handle_t) */
    int numFds;         /* number of file-descriptors at &data[0] */
    int numInts;        /* number of ints at &data[numFds] */
    int data[0];        /* numFds + numInts ints */
} native_handle_t;

    native_handle/native_handle_t只是定义了一个描述buffer的结构体原型,这个原型是和平台无关的,方便buffer在各个进程之间传递,注意成员data是一个大小为0的数组,这意味着data指向紧挨着numInts后面的一个地址.我们可以把native_handle_t看成是一个纯虚的基类.


    一般来说,我们描述一块buffer,需要知道它在kernel中对应的fd,虚拟地址/物理地址,offset,size等等信息,后面我们在private_handle_t中就可以看到这些字段.
    android的gralloc模块负责从fb设备或者gpu中分配meomory,所以我们在gralloc中就可以找到native_handle的具体实现,gralloc中对buffer的描述就和具体的平台相关了,我们以aosp中最基本的gralloc为例,来看下gralloc中对native_handle是如何使用的.


2.private_handle_t

hardware/libhardware/modules/gralloc/gralloc_priv.h

#ifdef __cplusplus
//在c++编译环境下private_handle_t继承于native_handle
struct private_handle_t : public native_handle {
#else
//在c编译环境下,private_handle_t的第一个成员是native_handle类型,其实和c++的继承是一个意思,
//总之就是一个指向private_handle_t的指针同样也可以表示一个指向native_handle的指针.
struct private_handle_t {
    struct native_handle nativeHandle;
#endif
    // file-descriptors
    int     fd; 
    // ints
    int     magic;
    int     flags;
    int     size;
    int     offset;
	// 因为native_handle的data成员是一个大小为0的数组,所以data[0]其实就是指向了fd,data[1]指向magic,以此类推.
	// 上面提到我们可以把native_handle看成是一个纯虚的基类,那么在private_handle_t这个派生类中,numFds=1 numInts=4.
	...
}
    gralloc分配的buffer都可以用一个private_handle_t来描述,同时也可以用一个native_handle来描述.在不同的平台的实现上,private_handle_t可能会有不同的定义,所以private_handle_t在各个模块之间传递的时候很不方便,而如果用native_handle的身份来传递,就可以消除平台的差异性.在HardwareComposer中,由SurfaceFlinger传给hwc的handle即是native_handle类型,而hwc作为平台相关的模块,他需要知道native_handle中各个字段的具体含义,所以hwc往往会将native_handle指针转化为private_handle_t指针来使用.

3. buffer_handle_t

    标题中并没有提到这个类型,因为这个类型实在是太简单了,我们看code

system/core/include/system/window.h

typedef const native_handle_t* buffer_handle_t;
    在window.h中又把指向native_handle_t的指针define为buffer_handle_t,

sturct native_handle

native_handle_t

struct private_handle_t
这三个类型可以看作是同一个东西,而buffer_handle_t则是指向他们的指针.

    那么android是如何使用这些struct的,gralloc分配的buffer如何和android联系起来呢?
我们继续来看window.h

4. ANativeWindowBuffer和ANativeWindow

    在具体分析ANativeWindowBuffer和ANativeWindow之前,我们先来看下和这两个类型都相关的另外一个结构体android_native_base_t

system/core/include/system/window.h

typedef struct android_native_base_t
{
    /* a magic value defined by the actual EGL native type */
    int magic;

    /* the sizeof() of the actual EGL native type */
    int version;

    void* reserved[4];

    /* reference-counting interface */
    void (*incRef)(struct android_native_base_t* base);
    void (*decRef)(struct android_native_base_t* base);
} android_native_base_t;
incRef和decRef是为了把派生类和android所有class的老祖宗RefBase联系起来所预留的函数指针,在后面我们在会看到指针具体会指向哪些函数.
typedef struct ANativeWindowBuffer
{
#ifdef __cplusplus
    ANativeWindowBuffer() {
		// ANDROID_NATIVE_BUFFER_MAGIC的值是"_bfr"
        common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
        common.version = sizeof(ANativeWindowBuffer);
        memset(common.reserved, 0, sizeof(common.reserved));
    }   

    // Implement the methods that sp<ANativeWindowBuffer> expects so that it
    // can be used to automatically refcount ANativeWindowBuffer's.
	// 调用common,也就是android_native_base_t的incRef和decRef函数,具体函数是什么还不知道
    void incStrong(const void* /*id*/) const {
        common.incRef(const_cast<android_native_base_t*>(&common));
    }   
    void decStrong(const void* /*id*/) const {
        common.decRef(const_cast<android_native_base_t*>(&common));
    }   
#endif

	// common的incRef和decRef还没有明确是什么
   struct android_native_base_t common;

    int width;
    int height;
    int stride;
    int format;
    int usage;

    void* reserved[2];
	// buffer_handle_t是指向sturct native_handle, native_handle_t, struct private_handle_t的指针.
    buffer_handle_t handle;

    void* reserved_proc[8];
} ANativeWindowBuffer_t;
// Old typedef for backwards compatibility.
typedef ANativeWindowBuffer_t android_native_buffer_t;
ANativeWindow的定义如下

struct ANativeWindow
{
#ifdef __cplusplus
    ANativeWindow()
        : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
    {
		// ANDROID_NATIVE_WINDOW_MAGIC的值是"_wnd"
        common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
        common.version = sizeof(ANativeWindow);
        memset(common.reserved, 0, sizeof(common.reserved));
    }

    /* Implement the methods that sp<ANativeWindow> expects so that it
       can be used to automatically refcount ANativeWindow's. */
	// 调用common,也就是android_native_base_t的incRef和decRef函数,具体函数是什么还不知道
    void incStrong(const void* /*id*/) const {
        common.incRef(const_cast<android_native_base_t*>(&common));
    }
    void decStrong(const void* /*id*/) const {
        common.decRef(const_cast<android_native_base_t*>(&common));
    }
#endif
	// common的incRef和decRef还没有明确是什么
    struct android_native_base_t common;
	...
	int     (*dequeueBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer** buffer, int* fenceFd);
	int     (*queueBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer* buffer, int fenceFd);
	int     (*cancelBuffer)(struct ANativeWindow* window,
                struct ANativeWindowBuffer* buffer, int fenceFd);
};
 /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C).
  * android_native_window_t is deprecated.
  */
typedef struct ANativeWindow ANativeWindow;
typedef struct ANativeWindow android_native_window_t __deprecated;
我们目前需要注意的是ANativeWindow的函数指针成员所指向的函数都需要一个struct ANativeWindowBuffer* buffer的参数.

ANativeWindowBuffer和ANativeWindow还是没有给android_native_base_t的incRef和decRef指针赋值,ANativeWindowBuffer和ANativeWindow两个还是可以理解为抽象类!


5. GraphicBuffer和Surface

frameworks/native/include/ui/GraphicBuffer.h 

class GraphicBuffer
    : public ANativeObjectBase< ANativeWindowBuffer, GraphicBuffer, RefBase >,
      public Flattenable<GraphicBuffer>
{
...
}
GraphicBuffer继承于模版类ANativeObjectBase,这个模版类有三个模版.

frameworks/native/include/ui/ANativeObjectBase.h

// NATIVE_TYPE=ANativeWindowBuffer TYPE=GraphicBuffer REF=RefBase
template <typename NATIVE_TYPE, typename TYPE, typename REF>
// ANativeObjectBase多重继承于ANativeWindowBuffer和RefBase
class ANativeObjectBase : public NATIVE_TYPE, public REF 
{
public:
    // Disambiguate between the incStrong in REF and NATIVE_TYPE
	// incStrong和decStrong直接调用其中一个基类RefBase的对应函数
    void incStrong(const void* id) const {
        REF::incStrong(id);
    }   
    void decStrong(const void* id) const {
        REF::decStrong(id);
    }   

protected:
	// 给ANativeObjectBase取了个别名BASE
    typedef ANativeObjectBase<NATIVE_TYPE, TYPE, REF> BASE;
    ANativeObjectBase() : NATIVE_TYPE(), REF() {
		// 构造函数中给ANativeWindowBuffer.common的两个函数指针赋值了!这两个指针就是我们之前在分析ANativeWindowBuffer的时候悬而未决的地方.
		// incRef和decRef指针分别指向内部函数incRef和decRef
        NATIVE_TYPE::common.incRef = incRef;
        NATIVE_TYPE::common.decRef = decRef;
    }   
    static inline TYPE* getSelf(NATIVE_TYPE* self) {
        return static_cast<TYPE*>(self);
    }   
    static inline TYPE const* getSelf(NATIVE_TYPE const* self) {
        return static_cast<TYPE const *>(self);
    }   
    static inline TYPE* getSelf(android_native_base_t* base) {
        return getSelf(reinterpret_cast<NATIVE_TYPE*>(base));
    }   
    static inline TYPE const * getSelf(android_native_base_t const* base) {
        return getSelf(reinterpret_cast<NATIVE_TYPE const*>(base));
    }
	// 内部函数incRef和decRef调用上面的incStong和decStrong,也就是说ANativeWindowBuffer.common的两个函数指针最终会调用到RefBase的incStrong和decStrong.
    static void incRef(android_native_base_t* base) {
        ANativeObjectBase* self = getSelf(base);
        self->incStrong(self);
    }   
    static void decRef(android_native_base_t* base) {
        ANativeObjectBase* self = getSelf(base);
        self->decStrong(self);
    } 
};
    搞了半天,原来GraphicBuffer就是ANativeWindowBuffer一种具体实现,把ANativeWindowBuffer的common成员的两个函数指针incRef decRef指向了GraphicBuffer的另一个基类RefBase的incStrong和decStrong,而ANativeWindowBuffer无非就是把buffer_handle_t包了一层.我们看下另外一个从ANativeObjectBase派生的类,他就是大名鼎鼎的,Surface!

frameworks/native/include/gui/Surface.h

class Surface
    : public ANativeObjectBase<ANativeWindow, Surface, RefBase>
{
	enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS };
    ...
	struct BufferSlot {
        sp<GraphicBuffer> buffer;
        Region dirtyRegion;
    };
	// mSlots stores the buffers that have been allocated for each buffer slot.
    // It is initialized to null pointers, and gets filled in with the result of
    // IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a
    // slot that has not yet been used. The buffer allocated to a slot will also
    // be replaced if the requested buffer usage or geometry differs from that
    // of the buffer allocated to a slot.
    BufferSlot mSlots[NUM_BUFFER_SLOTS];
	...
}

    Surface和GraphicBuffer都继承自模版类ANativeObjectBase,他使用的三个模版是ANativeWindow, Surface, RefBase,关于incRef和decRef两个函数指针的指向问题和上面GraphicBuffer是完全相同的, 这里就不赘述了.我们需要注意的是Surface有一个BufferSlot类型的成员数组mSlots,BufferSlot是GraphicBuffer的包装,所以我们可以理解为每个Surface中都有一个大小为NUM_BUFFER_SLOTS的GraphicBuffer数组.


    因为Surface继承自ANativeWindow,所以Surface需要实现ANativeWindow中定义的一些接口,这些实现在Surface的构造函数中:

frameworks/native/libs/gui/Surface.cpp

Surface::Surface(
        const sp<IGraphicBufferProducer>& bufferProducer,
        bool controlledByApp)
    : mGraphicBufferProducer(bufferProducer)
{
    // Initialize the ANativeWindow function pointers.
    ANativeWindow::setSwapInterval  = hook_setSwapInterval;
    ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
    ANativeWindow::cancelBuffer     = hook_cancelBuffer;
    ANativeWindow::queueBuffer      = hook_queueBuffer;
    ANativeWindow::query            = hook_query;
    ANativeWindow::perform          = hook_perform;

    ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
    ANativeWindow::cancelBuffer_DEPRECATED  = hook_cancelBuffer_DEPRECATED;
    ANativeWindow::lockBuffer_DEPRECATED    = hook_lockBuffer_DEPRECATED;
    ANativeWindow::queueBuffer_DEPRECATED   = hook_queueBuffer_DEPRECATED;

    const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
    const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
}
ANativeWindow定义的这些接口有什么用呢?谁会来call这些函数呢?举个例子来看.我们在EGL的api中可以找到eglCreateWindowSurface这个函数的定义:

frameworks/native/opengl/libs/EGL/eglApi.cpp

EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                    NativeWindowType window,
                                    const EGLint *attrib_list)
{
	...
}
注意其中一个参数NativeWindowType window,这个NativeWindowType又是什么呢?

frameworks/native/opengl/include/EGL/eglplatform.h

typedef struct ANativeWindow*           EGLNativeWindowType;
typedef EGLNativeWindowType  NativeWindowType;
原来NativeWindowType在Android环境下,就是ANativeWindow*,也就是Surface*!

总结一下,

native_handle/native_handle_t是private_handle_t的抽象表示方法,消除平台相关性,方便private_handle_t所表示的memory信息在android各个层次之间传递.而buffer_handle_t是指向他们的指针.

ANativeWindowBuffer将buffer_handle_t进行了包装,ANativeWindow和ANativeWindowBuffer都继承于android_native_base_t,定义了common.incRef和common.decRef两个函数指针,但是并没有为函数指针赋值,所以ANativeWindow和ANativeWindowBuffer仍然是抽象类.

GraphicBuffer和Surface通过继承模版类ANativeObjectBase并指定其中一个模版是RefBase,为incRef和decRef两个指针分别赋值为RefBase的incStrong和decStrong,这样

GraphicBuffer继承了ANativeWindowBuffer,Surface继承了ANativeWindow,并且两者都具有的和RefBase同样的incStong decStrong成员函数.

Surface的成员BufferSlot mSlots[NUM_BUFFER_SLOTS];可以看作是sp<GraphicBuffer>类型的数组,也就是说每个Surface中都包含有NUM_BUFFER_SLOTS个sp<GraphicBuffer>.


关于ANativeWindow的使用方法,我们可以在SurfaceFlinger中找到一个很好的列子,就是SF的captureScreen接口,我们下一篇文章继续讨论.





您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

<think>我们正在讨论的是AndroidUSB配件模式下的AccessoryDisplay实现逻辑。AccessoryDisplay是Android中用于支持通过USB配件模式进行显示输出的功能。根据用户的问题,我们需要深入分析AOSP中AccessoryDisplay的实现逻辑。首先,我们需要明确AccessoryDisplay的作用:它允许Android设备通过USB连接将显示内容输出到外部配件(如车载显示器、外接显示器等)。这通常用于汽车等场景,将手机屏幕内容投射到车载显示屏上。在AOSP中,AccessoryDisplay的实现主要涉及以下几个关键部分:1.**系统服务层**:在`com.android.server.display`包中,有一个`AccessoryDisplay`的服务,它负责管理显示适配器并处理与配件的通信。2.**USB通信层**:使用USB配件模式(AOA协议)建立连接,并通过USB传输显示数据。3.**SurfaceFlinger**:负责将显示内容渲染到虚拟显示设备上,然后通过USB发送到配件。###实现逻辑分析####1.初始化与配置当检测到支持显示输出的USB配件时,系统会启动AccessoryDisplay服务。该服务会读取配件的EDID(ExtendedDisplayIdentificationData)信息,以确定支持的显示模式(分辨率、刷新率等)。在`AccessoryDisplayAdapter`中,会创建一个虚拟显示设备(VirtualDisplay),该设备将作为配件显示的输出目标。```java//frameworks/base/services/core/java/com/android/server/display/AccessoryDisplayAdapter.javapublicfinalclassAccessoryDisplayAdapterextendsDisplayAdapter{@OverridepublicvoidregisterLocked(){//创建虚拟显示设备createVirtualDisplayLocked(...,"AccessoryDisplay",...);}}```####2.数据传输显示数据通过USB配件模式传输。在AOA协议中,Android设备作为USB配件(从设备),而外部配件作为主机。数据传输使用批量传输(BulkTransfer)或等时传输(IsochronousTransfer)来实现。在实现中,会有一个专门的线程负责从虚拟显示设备的Surface中读取帧数据,并通过USB输出流发送。```java//伪代码:数据传输线程publicclassDisplayTransferThreadextendsThread{privatefinalSurfacemSurface;privatefinalOutputStreammOutputStream;@Overridepublicvoidrun(){while(true){Imageimage=mSurface.lockCanvas();//获取当前帧byte[]data=compress(image);//压缩图像数据(如使用H.264)mOutputStream.write(data);//通过USB发送mSurface.unlockCanvasAndPost(image);}}}```####3.协议封装为了高效传输显示数据,通常会对图像进行压缩(如H.264编码),并封装成自定义协议。协议中可能包含帧头(时间戳、帧类型、数据长度等)和帧数据。```c//协议帧结构示例structDisplayFrame{uint32_ttimestamp;//时间戳uint16_twidth;//宽度uint16_theight;//高度uint32_tdataLength;//数据长度uint8_tdata[];//压缩后的图像数据};```####4.配件端实现配件端(主机)需要实现相应的接收和解码逻辑。配件端通过USB接收数据,解析协议帧,然后解码并显示。###关键代码路径-**AccessoryDisplay服务**:`frameworks/base/services/core/java/com/android/server/display/AccessoryDisplayAdapter.java`-**USB通信**:`frameworks/base/services/usb/java/com/android/server/usb/UsbAccessoryManager.java`-**虚拟显示**:`frameworks/base/core/java/android/hardware/display/DisplayManager.java`###配置与启用在设备配置中,需要启用USB配件模式支持,并配置相应的权限。在`init.rc`中可能会启动相关服务:```rc#允许访问USB配件模式setpropsys.usb.configaccessory```此外,在`device.mk`中需要包含相应的硬件支持:```makefile#启用配件显示功能PRODUCT_PACKAGES+=\android.hardware.usb.accessory```###性能优化为了减少延迟和提高传输效率,通常采用以下优化:1.**硬件编码**:使用硬编码器(如MediaCodec)压缩图像。2.**双缓冲**:在Surface中使用双缓冲机制避免画面撕裂。3.**丢帧策略**:当传输跟不上时,丢弃非关键帧(如P帧),保留关键帧(I帧)。###相关问题1.AccessoryDisplay如何与SurfaceFlinger交互?2.如何配置EDID信息以支持多种显示模式?3.在USB配件模式下,如何实现音频和显示数据的同步传输?4.AccessoryDisplay支持哪些图像压缩格式?5.如何测试AccessoryDisplay的传输延迟?>参考文档:>[^1]:AndroidOpenSourceProject-AccessoryDisplay>[^2]:USBAccessoryDeveloperGuide>[^3]:AndroidVirtualDisplayImplementation</think>### AOSP AccessoryDisplay 实现逻辑分析 AccessoryDisplay 是 Android USB 配件模式(Accessory Mode)中的关键组件,用于实现通过 USB 连接的显示输出功能。其核心实现逻辑如下: #### 1. 架构层次 ```mermaid graph TD A[应用层] --> B[DisplayManagerService] B --> C[AccessoryDisplayAdapter] C --> D[VirtualDisplay] D --> E[SurfaceFlinger] E --> F[USB 配件模式传输层] F --> G[USB 硬件驱动] ``` #### 2. 核心组件实现 **AccessoryDisplayAdapter** (`frameworks/base/services/core/java/com/android/server/display/AccessoryDisplayAdapter.java`): ```java public class AccessoryDisplayAdapter extends DisplayAdapter { private final Handler mHandler; private final List<AccessoryDisplayDevice> mDevices = new ArrayList<>(); // 检测新配件设备 public void handleDeviceAdded(AccessoryDisplayDevice device) { synchronized (mSyncRoot) { // 创建虚拟显示设备 VirtualDisplay virtualDisplay = createVirtualDisplay(...); mDevices.add(device); } } // 创建虚拟显示 private VirtualDisplay createVirtualDisplay(DisplayDeviceInfo info) { return mDisplayManagerInternal.createVirtualDisplay(...); } } ``` **USB 配件模式服务** (`frameworks/base/services/usb/java/com/android/server/usb/UsbAccessoryManager.java`): ```java public class UsbAccessoryManager { public void accessoryAttached(UsbAccessory accessory) { // 检测到配件设备 if (isDisplayAccessory(accessory)) { // 通知显示服务 mDisplayManager.handleAccessoryDisplayAttached(); } } } ``` #### 3. 显示数据传输流程 1. **Surface 绑定**: ```java // 在 SurfaceFlinger 中创建虚拟显示 sp<IBinder> displayToken = mFlinger->createDisplay(...); sp<Surface> surface = new Surface(producer); ``` 2. **帧数据捕获**: ```c++ // SurfaceFlinger 合成流程 void SurfaceFlinger::composite() { if (isAccessoryDisplay(display)) { captureFrameToBuffer(display); // 捕获帧数据 } } ``` 3. **USB 传输协议**: ```c // USB 端点数据传输 struct accessory_packet { uint32_t frame_number; uint64_t timestamp; uint32_t data_size; uint8_t payload[0]; }; void send_frame_over_usb(struct accessory_packet* packet) { libusb_bulk_transfer(handle, EP_OUT, packet, sizeof(struct accessory_packet) + packet->data_size, ...); } ``` #### 4. 关键配置文件 **设备端配置** (`device/qcom/.../init.target.rc`): ```rc # 启用配件显示服务 service accessory_display /system/bin/accessory_display_service class main user system group usb ``` **权限配置** (`frameworks/native/data/etc/android.hardware.usb.accessory.xml`): ```xml <permissions> <feature name="android.hardware.usb.accessory" /> <feature name="android.hardware.display.accessory" /> </permissions> ``` #### 5. 性能优化技术 1. **零拷贝传输**: ```cpp // 直接使用图形缓冲区 GraphicBuffer* buffer = surface->dequeueBuffer(); libusb_bulk_transfer(handle, EP_OUT, buffer->handle, ...); ``` 2. **动态分辨率调整**: ```java // 根据带宽自动调整 void adjustResolution(int usbSpeed) { if (usbSpeed == USB_SPEED_SUPER) { setResolution(1920, 1080); } else { setResolution(1280, 720); } } ``` 3. **硬件加速编码**: ```cpp // 使用 MediaCodec 硬件编码 MediaCodec.createEncoderByType("video/avc"); ``` #### 6. 兼容性实现 ```mermaid graph LR A[Android 8.0+] -->|标准实现| B[AccessoryDisplay] C[Android 7.x] -->|兼容层| D[LegacyAccessoryAdapter] E[定制ROM] -->|厂商扩展| F[CustomAccessoryService] ``` ### 实现注意事项 1. **权限控制**: ```xml <!-- AndroidManifest.xml --> <uses-permission android:name="android.permission.MANAGE_USB"/> <uses-permission android:name="android.permission.ACCESSORY_DISPLAY"/> ``` 2. **EDID 处理**: ```c // 解析显示器能力数据 void parse_edid(const uint8_t* edid_data) { // 解析分辨率支持列表 } ``` 3. **热插拔支持**: ```java // 监听 USB 状态变化 UsbManager.registerReceiver(new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { // 处理配件连接/断开 } }); ``` --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值