久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 12669|回復: 1
打印 上一主題 下一主題
收起左側

學習筆記-貫通 Android 底層驅動至應用層APP接口流程 代碼分析

[復制鏈接]
跳轉到指定樓層
樓主
ID:91350 發表于 2015-9-30 01:04 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
以下例子均為未經測試的代碼,也重點在整個的流程概況。目前仍有些不是太明白的地方。

分以下幾點(忽略HAL層):


驅動:lichee/linux-3.4/drivers/
        主要是初始化相關芯片以及具體的交互功能,然后創建設備節點來與上層交互。(盡量不做邏輯控制)
        調用 class_register() 接口時,會在 /sys/class/ 目錄下創建設備節點。
        調用 misc_register()  接口時,會在 /dev/ 目錄下創建設備節點。
        
JNI:android/frameworks/base/services/jni/
        該層一般是提供JAVA與 C\C++ 之間通信,這一層我主要用來做一些基本的邏輯控制處理,記得之前有
        次參加面試,該公司主要是做類似安防的手機,他們就是主要在這層做邏輯控制。該層主要是通過
        映射表來描述 JAVA 方法與 C\C++ 函數之間的對應關系。
        static JNINativeMethod method_table[] ={
                //  JAVA 方法                返回值與參數描述        C\C++ 函數
                {"Xxx_Init_native",             "()I",                    (void *)HardwareConfig_XxxInit},
                {"Xxx_Cmd_native",                 "(II)I",                   (void *)HardwareConfig_XxxCmd},
        };


Server:android/frameworks/base/services/java/com/android/server/SystemServer.java
        這個主要是供其他 APP 通過 aidl 接口調用到 Server 方法,Server 方法通過JNI映射表,
        調用JNI的函數。進一步封裝接口,實現必要的邏輯處理。


aidl:frameworks/base/core/java/android/os
        該文件用來生成 Stub 接口文件,用來聲明在 Server 提供的接口。
        
SystemServer:創建 Server 實例,加入服務表中,供 APP 獲取使用。


接口:
        聲明一些數據結構、獲取 Server 根據需要決定是否再進一步封裝接口。
        其主要的目的是為了APP能夠更加方便的使用接口。
        
APP:
        通過接口文件,來調用相關的接口從而間接調用底層驅動功能。


調用流程:APP 通過獲取AIDL通信,獲取服務,通過服務調用服務的提供的類,調用JNI接口、JNI再去調用驅動。
        
具體例子:


/////////////////////////////////////////////////////////////////////////////////////////////
驅動:lichee/linux-3.4/drivers/my_drivers/pt22xx.c
        會創建一個設備節點:/dev/MicAdjust 用于給上層交互
/////////////////////////////////////////////////////////////////////////////////////////////
        long Pt22xx_ioctl(struct file *file, unsigned int cmd, unsigned long args)
        {
                unsigned char ret = 0;
               
                if (_IOC_TYPE(cmd) != MAGIC)
                        return -EINVAL;


                switch(cmd)
                {
                        case CMD_SETMICVOL:                                        dprintk("CMD_SETMICVOL....");
                                g_CurrentMicVolume = args;
                                // 與底層硬件進行交互
                                break;
                                
                        case CMD_SETMICECHO:                                dprintk("CMD_SETMICECHO....");
                                g_CurrentMicEcho = args;
                                // 與底層硬件進行交互
                                break;


                        case CMD_GETMICVOL:                                dprintk("CMD_GETMICECHO....");
                                ret = g_CurrentMicVolume;
                                break;
                                
                        case CMD_GETMICECHO:                                dprintk("CMD_GETMICECHO....");
                                ret = g_CurrentMicEcho;
                                break;
                                
                        default:                                                        dprintk("no cmd....");
                                return -EINVAL;
                }
               
                return ret;
        }




        static struct file_operations Pt22xx_fops = {
                .owner                            =    THIS_MODULE,
                .unlocked_ioctl            =    Pt22xx_ioctl,
        };


        static struct miscdevice Pt22xx_misc =
        {
                .minor = MISC_DYNAMIC_MINOR,
                .name = "MicAdjust",
                .fops = &Pt22xx_fops,
        };


        /*************************** 驅動入口 驅動出口 ***************************/
         static int __init Pt22xx_init(void)
        {
                misc_register(&Pt22xx_misc);
                return 0;
        }


        static void __exit Pt22xx_exit(void)
        {


        }
        /*************************** 驅動入口 驅動出口 ***************************/


        module_init(Pt22xx_init);
        module_exit(Pt22xx_exit);
        MODULE_LICENSE("GPL");
/////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////////
Jni:android/frameworks/base/services/jni/my_jni/Mic.cpp
        會創建一個設備節點:/dev/MicAdjust 用于給上層交互
/////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------
        對應驅動的JNI文件
//-------------------------------------------------------------------------------------


// 功放命令實現
jint Mic_Cmd(JNIEnv *env, jobject thiz,  jint cmd, jint arg1)
{
        int ret = 0;
        // 這里則通過識別命令和參數 通過 ioctl 函數設置驅動
        // 這里應用層 ioctl() 實際上回調的是驅動層 -> Pt22xx_ioctl() 函數
        return ret;
}


// 初始化函數,需要識別并初始化硬件設備
jint Mic_Init(JNIEnv *env, jobject thiz)
{
        // 初始化硬件
        // 這里會 open /dev/MicAdjust  設備節點
        return 0;
}


//-------------------------------------------------------------------------------------
        管理和協調各個JNI程序,實現一些基本的邏輯功能,例如參數保存之類的。。
        android/frameworks/base/services/jni/com_android_server_HardwareConfig.cpp
//-------------------------------------------------------------------------------------
namespace android {
        /* 一些數據結構 */
        
        ///////////////////////////// AMP 邏輯實現 ////////////////////////////////////////
        /* 處理獲取命令 */
        jint HardwareConfig_GetAmp(enum enAmp_CMDTYPE cmd)
        {
                int ret;        
                switch(cmd)
                {


                        default:
                                return eAMP_CMD_ERROR;
                }
               
                return ret;
        }
        
        // 功放命令實現
        jint HardwareConfig_AmpCmd(JNIEnv *env, jobject thiz,  jint cmd, jint arg1)
        {
                int ret = 0;


                // 非法參數檢查
               
                // 判斷是否為獲取命令
                if( IsAmpGetCmdType(cmd) )
                {
                        return HardwareConfig_GetAmp((enum enAmp_CMDTYPE)cmd);
                }
               
                // 參數檢查并糾正


                // 調用相應的功能函數
                ret = Amp_Cmd((enAmp_CMDTYPE)cmd, (enAmp_ARGSTYPE)arg1);
               
                /* 保存相關參數 */
                return ret;
        }
        
        ///////////////////////////// MIC 邏輯實現 ////////////////////////////////////////
        jint HardwareConfig_GetMic(enum enMic_CMDTYPE cmd)
        {
                int ret;


                switch((int)cmd)
                {
                        default:
                                return eMIC_CMD_ERROR;
                }
               
                return ret;        
        }
        
        
        jint HardwareConfig_MicCmd(JNIEnv *env, jobject thiz,  jint cmd, jint arg1)
        {
                int ret = 0;
               
                // 非法參數檢查
               
                // 判斷是否為獲取命令
                if( IsAmpGetCmdType(cmd) )
                {
                        return HardwareConfig_GetAmp((enum enAmp_CMDTYPE)cmd);
                }
               
                // 參數檢查并糾正


                // 調用相應的功能函數
                ret = Amp_Cmd((enAmp_CMDTYPE)cmd, (enAmp_ARGSTYPE)arg1);
               
                /* 保存相關參數 */
                return ret;
        }
        
        ///////////////////////////// 以上為邏輯功能實現 ////////////////////////////////////////
        
        
        // 初始化函數,需要識別并初始化硬件設備 這里對硬件初始化主要是調用各自的JNI接口初始化的
        jint HardwareConfig_Init(JNIEnv *env, jobject thiz)
        {
                // 讀取配置文件
               
                /* 功放初始化 */
                Amp_Init();
                Amp_Cmd(...);        
                Amp_Cmd(...);        
                Amp_Cmd(...);
               
                /* 初始化麥克風 */               
                Mic_Init();
                Mic_Cmd(...);
                Mic_Cmd(...);
               
                /* 初始化其他硬件 */
                return 0;
        }


        ///////////////////////////// JNI ////////////////////////////////////////
        // 構建映射表
        static JNINativeMethod method_table[] ={
                {"HardwareConfig_Init_native",             "()I",      (void *)HardwareConfig_Init},
                {"Amp_Cmd_native",                 "(II)I",   (void *)HardwareConfig_AmpCmd},
                {"Mic_Cmd_native",                 "(II)I",   (void *)HardwareConfig_MicCmd},
                //.... 其他硬件
        };
        
        int register_android_server_HardwareConfigService(JNIEnv* env)
        {
                return jniRegisterNativeMethods(env,"com/android/server/HardwareConfigService", method_table, NELEM(method_table));
        }
}
//-------------------------------------------------------------------------------------
        修改 onload.cpp 文件,該文件是用來集中加載所有Jni。
        android\frameworks\base\services\jni\onload.cpp
//-------------------------------------------------------------------------------------
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"


namespace android {
//... 省略
int register_android_server_HardwareConfigService(JNIEnv* env);
};


using namespace android;


extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
        //... 省略
        register_android_server_HardwareConfigService(env);
        
    return JNI_VERSION_1_4;
}


//-------------------------------------------------------------------------------------
        修改 對應目錄下的 Android.mk 文件,該文件是用來編譯這些Jni文件的
        android\frameworks\base\services\jni\Android.mk
//-------------------------------------------------------------------------------------
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)


LOCAL_SRC_FILES:= \
    // ... 省略
        zhc_jni/Mic.cpp \
        com_android_server_HardwareConfig.cpp \
    onload.cpp


LOCAL_C_INCLUDES += \
        // ... 省略
        my_jni


ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
    LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endif


LOCAL_MODULE:= libandroid_servers


include $(BUILD_SHARED_LIBRARY)


/*************** 完成以上步驟就可以在當前目錄下通過 mm 命令編譯出 JNI SO庫文件了 ***********/
/////////////////////////////////////////////////////////////////////////////////////////////
Server:
        在 android\frameworks\base\services\java\com\android\server 建立一個文件 HardwareConfigService.java
/////////////////////////////////////////////////////////////////////////////////////////////
package com.android.server;


import android.content.Context;
import android.util.Slog;
import android.content.Context;


public class HardwareConfigService extends IHardwareConfigService.Stub {


    private static final String TAG = "HardwareConfigService";
        private static native int HardwareConfig_Init_native();
        private static native int Amp_Cmd_native(int cmd, int arg1);
        private static native int Mic_Cmd_native(int cmd, int arg1);
        private static Context mContext;


        /////////////////////////////////////  初始化處理部分 ////////////////////////////////
        // 當這個類被創建時,以下代碼將會被執行
    public HardwareConfigService(Context context)
    {
                mContext = context;
        HardwareConfig_Init_native();        // <---- 這里調用 JNI 部分接口 進行初始化
    }
        
        ///////////////////////////////////// 接口定義部分 ////////////////////////////////
        public int Amp_Cmd(int Cmd, int arg1)                // <<---- 對外提供的調用的接口
        {
                int ret = 0;
                // 在這里可以對接口做進一步封裝
                ret = Amp_Cmd_native(Cmd, arg1);                // <---- 這里調用 JNI 部分接口
                return ret;
        }


        public int Mic_Cmd(int Cmd, int arg1)                // <<---- 對外提供的調用的接口
        {
                int ret = 0;
                // 在這里可以對接口做進一步封裝
                ret = Mic_Cmd_native(Cmd, arg1);                // <---- 這里調用 JNI 部分接口
                return ret;
        }
        
};


//-------------------------------------------------------------------------------------
        修改同目錄下的SystemServer.java文件,在ServerThread::run方法里加入
//-------------------------------------------------------------------------------------
class ServerThread extends Thread {
    private static final String TAG = "SystemServer";
    private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
        // ... 省略代碼
    @Override
    public void run() {

                try{        // 在這里 new 了一個對象并添加到 ServiceManager 中。
                        Slog.i(TAG, "add HardwareConfigService");
                        ServiceManager.addService("HardwareConfigService", new HardwareConfigService(context));
                } catch (RuntimeException e) {
                        Slog.e("System", "******************************************");
                        Slog.e("System", "************ Failure starting HardwareConfigService", e);
                }
        // ... 省略代碼
    }
}


public class SystemServer {
    private static final String TAG = "SystemServer";
        
        // ... 省略代碼
    public static final void init2() {
        Slog.i(TAG, "Entered the Android system server!");
        Thread thr = new ServerThread();
        thr.setName("android.server.ServerThread");
        thr.start();
    }
}


/*************** 完成以上步驟就可以在 android\frameworks\base\services\java 下通過 mm 命令編譯了 ***********/


/////////////////////////////////////////////////////////////////////////////////////////////
AIAL:在 frameworks/base/core/java/android/os/ 新建立一個文件 IHardwareConfigService.aidl
/////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------
        加入以下內容,以下內容為 服務里面的public接口,即提供給APP使用的接口
//-------------------------------------------------------------------------------------
package android.os;
interface IHardwareConfigService
{
        int Amp_Cmd(int Cmd, int arg1);
        int Mic_Cmd(int Cmd, int arg1);
}
//-------------------------------------------------------------------------------------
        返回到 frameworks/base 修改 Android.mk 文件
//-------------------------------------------------------------------------------------
LOCAL_SRC_FILES += \
        core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
        ## 省略
        core/java/android/os/IHardwareConfigService.aidl \ ## 增加這一行代碼
# Include subdirectory makefiles




/////////////////////////////////////////////////////////////////////////////////////////////
接口文件:這個接口文件主要是進一步封裝接口,把一些命令標識什么的都放到問個文件里面
        在 android\frameworks\base\policy\src\com\android\internal\policy\impl
        新建立一個文件 HardwareConfigServiceInterface.java
/////////////////////////////////////////////////////////////////////////////////////////////
package com.android.internal.policy.impl;


import android.os.IHardwareConfigService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;


public class HardwareConfigServiceInterface {
        
        private IHardwareConfigService HardwareConfigService = null;
        static final String TAG = "HardwareConfigServiceInterface";
        
        ///////////////////////// 功放命令與參數定義 ////////////////////////////        
        public enum enAmp_CMDTYPE                  
        {   
                // 設置命令
                eAMP_CMD_OPEN,                                                // 打開
                eAMP_CMD_CLOSE,                                                // 關閉
                eAMP_CMD_SETMUTE,                                        // 靜音
                eAMP_CMD_SETVOLUME,                                        // 設置音量
                eAMP_CMD_SETEQ,                                                // 設置音效
                eAMP_CMD_SETSIGLEEND,                                // 設置立體聲
                eAMP_CMD_SETSURROUND,                                // 設置環繞
                eAMP_CMD_SETTREBLEBASS_CTRL,                // 高低音控制        
                eAMP_CMD_SETTREBLE,                                        // 設置高音
                eAMP_CMD_SETBASS,                                        // 設置低音
                // .........
                eAMP_CMD_ERROR,                                                // 命令執行錯誤或失敗,同時也是命令的個數
        };
        ///////////////////////// 功放接口 ////////////////////////////        
        
        /* 獲取某些命令是已經使能了還是關閉了 設計此接口是為了應用層更加方便的判斷 出錯返回 eAMP_ARGS_NOT_USER*/
        public enAmp_ARGSTYPE Amp_GetCmd(enAmp_CMDTYPE Cmd)
        {
                int index = 0;
                // 省略轉換代碼
                index = Amp_Cmd(Cmd, enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER.ordinal());
                // 省略轉換代碼
                return enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER;        
        }


        /* 獲取具體的值 例如 高低音的值 出錯返回 -1 */
        public int Amp_GetData(enAmp_CMDTYPE Cmd)
        {
                int ret = Amp_Cmd(Cmd, enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER.ordinal());
                // 省略轉換代碼
                return ret;
        }
        
        public enAmp_CMDTYPE Amp_SetCmd(enAmp_CMDTYPE Cmd, enAmp_ARGSTYPE Args)
        {        
                int index = 0;
                // 省略轉換代碼
                index = Amp_Cmd(Cmd, Args.ordinal());
                // 省略轉換代碼
                return enAmp_CMDTYPE.eAMP_CMD_ERROR;
        }
        
        public int Amp_SetData(enAmp_CMDTYPE Cmd, int Args)
        {        
                Log.d(TAG, "Amp_SetData() Cmd = " + Cmd + "Args = " + Args );
                return  Amp_Cmd(Cmd, Args);
        }
        
        /* 最終的實現是由此命令完成的 設置命令時出錯返回 eAMP_CMD_ERROR 的索引*/
        private int Amp_Cmd(enAmp_CMDTYPE Cmd, int Args)
        {
                Log.d(TAG, "Amp_SetCmd() Cmd = " + Cmd + " ordinal = " + Cmd.ordinal());
                Log.d(TAG, "Amp_SetCmd() Args = " + Args);
                try
                {
                        if(null == HardwareConfigService)
                        {
                                HardwareConfigService = IHardwareConfigService.Stub.asInterface(ServiceManager.getService("HardwareConfigService"));
                        }
                        
                        return HardwareConfigService.Amp_Cmd(Cmd.ordinal(), Args);
                        
                } catch (RemoteException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        return (int)(enAmp_CMDTYPE.eAMP_CMD_ERROR.ordinal());
                }
               
        }
        
        ///////////////////////// 麥克風接口與定義 ////////////////////////////        
        
        /* 功放命令集,具體命令需由具體的功放芯片驅動實現 */
        public enum enMic_CMDTYPE                  
        {   
                // 設置命令
                eMIC_CMD_START        ,                                // 起始
                eMIC_CMD_SETVOLUME,                                        // 設置MIC音量
                eMIC_CMD_SETECHO,                                        // 設置MIC回響
                eMIC_CMD_SETMUTE,                                        // 麥克風靜音
               
                // 獲取命令
                eMIC_CMD_GETTYPE_START,                                // 獲取命令類型 用于做標識 當大于此值時表示是獲取命令類型
                eMIC_CMD_GETVOLUME,                                        // 獲取MIC音量值
                eMIC_CMD_GETECHO,                                        // 獲取MIC回響值
                eMIC_CMD_GETMUTE,
                eMIC_CMD_GETTYPE_STOP,                                // eMic_CMD_GETTYPE_START 與 eMic_CMD_GETTYPE_STOP 之間是獲取命令的類型
               
                eMIC_CMD_ERROR,                                                // 命令執行錯誤或失敗,同時也是命令的個數
        };


        public enum enMic_ARGSTYPE                                                // 參數
        {
                eMIC_ARGS_DISABLE ,
                eMIC_ARGS_ENABLE,
               
                eMIC_ARGS_NOT_USER,                                        // 不使用
        };
        ///////////////////////// 麥克風接口實現 ////////////////////////////
                /* 獲取某些命令是已經使能了還是關閉了 設計此接口是為了應用層更加方便的判斷 出錯返回 eMIC_ARGS_NOT_USER*/
        public enMic_ARGSTYPE Mic_GetCmd(enMic_CMDTYPE Cmd)
        {
                int index = 0;
                // 省略轉換代碼
                index = Mic_Cmd(Cmd, enMic_ARGSTYPE.eMIC_ARGS_NOT_USER.ordinal());
                // 省略轉換代碼
                return enMic_ARGSTYPE.eMIC_ARGS_NOT_USER;        
        }


        /* 獲取具體的值 出錯返回 -1 */
        public int Mic_GetData(enMic_CMDTYPE Cmd)
        {
                int ret = Mic_Cmd(Cmd, enMic_ARGSTYPE.eMIC_ARGS_NOT_USER.ordinal());
                // 省略轉換代碼
                return ret;
        }
        
        public enMic_CMDTYPE Mic_SetCmd(enMic_CMDTYPE Cmd, enMic_ARGSTYPE Args)
        {        
                int index = 0;
                // 省略轉換代碼
                index = Mic_Cmd(Cmd, Args.ordinal());
                // 省略轉換代碼
                return enMic_CMDTYPE.eMIC_CMD_ERROR;
        }
        
        public int Mic_SetData(enMic_CMDTYPE Cmd, int Args)
        {        
                // 省略轉換代碼
                return  Mic_Cmd(Cmd, Args);
        }
        
        /* 最終的實現是由此命令完成的 設置命令時出錯返回 eMIC_CMD_ERROR 的索引*/
        private int Mic_Cmd(enMic_CMDTYPE Cmd, int Args)
        {
                Log.d(TAG, "Mic_SetCmd() Cmd = " + Cmd + " ordinal = " + Cmd.ordinal());
                Log.d(TAG, "Mic_SetCmd() Args = " + Args);
                try
                {
                        if(null == HardwareConfigService)
                        {
                                HardwareConfigService = IHardwareConfigService.Stub.asInterface(ServiceManager.getService("HardwareConfigService"));
                        }
                        
                        return HardwareConfigService.Mic_Cmd(Cmd.ordinal(), Args);
                        
                } catch (RemoteException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        return (int)(enMic_CMDTYPE.eMIC_CMD_ERROR.ordinal());
                }
               
        }


}


/*****************************************************************************
        昨晚以上的操作之后需要 make updata-api 建議全編一次
*****************************************************************************/


/////////////////////////////////////////////////////////////////////////////////////////////
應用層使用方法:
/////////////////////////////////////////////////////////////////////////////////////////////
1、需要在 Android 工程中加上 android\out\target\common\obj\JAVA_LIBRARIES\android.policy_intermediates\classes.jar


2、導入需要的數據結構
import com.android.internal.policy.impl.HardwareConfigServiceInterface;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enAmp_CMDTYPE;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enAmp_ARGSTYPE;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enMic_CMDTYPE;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enMic_ARGSTYPE;
import com.android.internal.policy.impl.HardwareConfigServiceInterface.enDevices_Status;


3、定義全局變量
        static private HardwareConfigServiceInterface HardwareConfig = null;
4、在初始化函數中new 對象
        HardwareConfig = new HardwareConfigServiceInterface();


5、在需要的地方直接調用接口
        HardwareConfig.Amp_GetCmd(enAmp_CMDTYPE.eAMP_CMD_GETSDMUTE)


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:93145 發表于 2015-10-21 18:34 | 只看該作者
謝謝~
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 一级无毛片 | 欧美日韩精品 | 在线免费视频一区 | 色www精品视频在线观看 | 亚洲欧洲在线观看视频 | 精品国产精品三级精品av网址 | 欧美日韩亚洲在线 | 欧美国产日韩在线观看 | 久草网址 | 99色综合| 精品无码久久久久久国产 | 午夜精品久久 | 91精品久久久 | 日韩在线观看视频一区 | 国产成人99久久亚洲综合精品 | 青青久久久 | 国产在线观看免费 | 亚洲视频中文字幕 | 亚洲综合婷婷 | 欧美高清性xxxxhd| 日本在线免费观看 | 日韩a在线 | 91视频在线 | caoporn免费| 久久精品视频免费看 | 永久www成人看片 | 毛片毛片毛片毛片毛片 | 91精品国产综合久久久亚洲 | 久久国色 | wwwxxx国产| 中文字幕国产 | 国产精品久久久爽爽爽麻豆色哟哟 | 激情小说综合网 | 99热在线观看精品 | 99这里只有精品视频 | 亚洲婷婷一区 | 国产高清区 | 亚洲国产欧美一区二区三区久久 | 国产精品国产精品国产专区不蜜 | 日韩久久中文字幕 | 精品亚洲一区二区三区四区五区高 |