|
以下例子均為未經(jīng)測(cè)試的代碼,也重點(diǎn)在整個(gè)的流程概況。目前仍有些不是太明白的地方。
分以下幾點(diǎn)(忽略HAL層):
驅(qū)動(dòng):lichee/linux-3.4/drivers/
主要是初始化相關(guān)芯片以及具體的交互功能,然后創(chuàng)建設(shè)備節(jié)點(diǎn)來(lái)與上層交互。(盡量不做邏輯控制)
調(diào)用 class_register() 接口時(shí),會(huì)在 /sys/class/ 目錄下創(chuàng)建設(shè)備節(jié)點(diǎn)。
調(diào)用 misc_register() 接口時(shí),會(huì)在 /dev/ 目錄下創(chuàng)建設(shè)備節(jié)點(diǎn)。
JNI:android/frameworks/base/services/jni/
該層一般是提供JAVA與 C\C++ 之間通信,這一層我主要用來(lái)做一些基本的邏輯控制處理,記得之前有
次參加面試,該公司主要是做類似安防的手機(jī),他們就是主要在這層做邏輯控制。該層主要是通過(guò)
映射表來(lái)描述 JAVA 方法與 C\C++ 函數(shù)之間的對(duì)應(yīng)關(guān)系。
static JNINativeMethod method_table[] ={
// JAVA 方法 返回值與參數(shù)描述 C\C++ 函數(shù)
{"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
這個(gè)主要是供其他 APP 通過(guò) aidl 接口調(diào)用到 Server 方法,Server 方法通過(guò)JNI映射表,
調(diào)用JNI的函數(shù)。進(jìn)一步封裝接口,實(shí)現(xiàn)必要的邏輯處理。
aidl:frameworks/base/core/java/android/os
該文件用來(lái)生成 Stub 接口文件,用來(lái)聲明在 Server 提供的接口。
SystemServer:創(chuàng)建 Server 實(shí)例,加入服務(wù)表中,供 APP 獲取使用。
接口:
聲明一些數(shù)據(jù)結(jié)構(gòu)、獲取 Server 根據(jù)需要決定是否再進(jìn)一步封裝接口。
其主要的目的是為了APP能夠更加方便的使用接口。
APP:
通過(guò)接口文件,來(lái)調(diào)用相關(guān)的接口從而間接調(diào)用底層驅(qū)動(dòng)功能。
調(diào)用流程:APP 通過(guò)獲取AIDL通信,獲取服務(wù),通過(guò)服務(wù)調(diào)用服務(wù)的提供的類,調(diào)用JNI接口、JNI再去調(diào)用驅(qū)動(dòng)。
具體例子:
/////////////////////////////////////////////////////////////////////////////////////////////
驅(qū)動(dòng):lichee/linux-3.4/drivers/my_drivers/pt22xx.c
會(huì)創(chuàng)建一個(gè)設(shè)備節(jié)點(diǎn):/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;
// 與底層硬件進(jìn)行交互
break;
case CMD_SETMICECHO: dprintk("CMD_SETMICECHO....");
g_CurrentMicEcho = args;
// 與底層硬件進(jìn)行交互
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,
};
/*************************** 驅(qū)動(dòng)入口 驅(qū)動(dòng)出口 ***************************/
static int __init Pt22xx_init(void)
{
misc_register(&Pt22xx_misc);
return 0;
}
static void __exit Pt22xx_exit(void)
{
}
/*************************** 驅(qū)動(dòng)入口 驅(qū)動(dòng)出口 ***************************/
module_init(Pt22xx_init);
module_exit(Pt22xx_exit);
MODULE_LICENSE("GPL");
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
Jni:android/frameworks/base/services/jni/my_jni/Mic.cpp
會(huì)創(chuàng)建一個(gè)設(shè)備節(jié)點(diǎn):/dev/MicAdjust 用于給上層交互
/////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------
對(duì)應(yīng)驅(qū)動(dòng)的JNI文件
//-------------------------------------------------------------------------------------
// 功放命令實(shí)現(xiàn)
jint Mic_Cmd(JNIEnv *env, jobject thiz, jint cmd, jint arg1)
{
int ret = 0;
// 這里則通過(guò)識(shí)別命令和參數(shù) 通過(guò) ioctl 函數(shù)設(shè)置驅(qū)動(dòng)
// 這里應(yīng)用層 ioctl() 實(shí)際上回調(diào)的是驅(qū)動(dòng)層 -> Pt22xx_ioctl() 函數(shù)
return ret;
}
// 初始化函數(shù),需要識(shí)別并初始化硬件設(shè)備
jint Mic_Init(JNIEnv *env, jobject thiz)
{
// 初始化硬件
// 這里會(huì) open /dev/MicAdjust 設(shè)備節(jié)點(diǎn)
return 0;
}
//-------------------------------------------------------------------------------------
管理和協(xié)調(diào)各個(gè)JNI程序,實(shí)現(xiàn)一些基本的邏輯功能,例如參數(shù)保存之類的。。
android/frameworks/base/services/jni/com_android_server_HardwareConfig.cpp
//-------------------------------------------------------------------------------------
namespace android {
/* 一些數(shù)據(jù)結(jié)構(gòu) */
///////////////////////////// AMP 邏輯實(shí)現(xiàn) ////////////////////////////////////////
/* 處理獲取命令 */
jint HardwareConfig_GetAmp(enum enAmp_CMDTYPE cmd)
{
int ret;
switch(cmd)
{
default:
return eAMP_CMD_ERROR;
}
return ret;
}
// 功放命令實(shí)現(xiàn)
jint HardwareConfig_AmpCmd(JNIEnv *env, jobject thiz, jint cmd, jint arg1)
{
int ret = 0;
// 非法參數(shù)檢查
// 判斷是否為獲取命令
if( IsAmpGetCmdType(cmd) )
{
return HardwareConfig_GetAmp((enum enAmp_CMDTYPE)cmd);
}
// 參數(shù)檢查并糾正
// 調(diào)用相應(yīng)的功能函數(shù)
ret = Amp_Cmd((enAmp_CMDTYPE)cmd, (enAmp_ARGSTYPE)arg1);
/* 保存相關(guān)參數(shù) */
return ret;
}
///////////////////////////// MIC 邏輯實(shí)現(xiàn) ////////////////////////////////////////
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;
// 非法參數(shù)檢查
// 判斷是否為獲取命令
if( IsAmpGetCmdType(cmd) )
{
return HardwareConfig_GetAmp((enum enAmp_CMDTYPE)cmd);
}
// 參數(shù)檢查并糾正
// 調(diào)用相應(yīng)的功能函數(shù)
ret = Amp_Cmd((enAmp_CMDTYPE)cmd, (enAmp_ARGSTYPE)arg1);
/* 保存相關(guān)參數(shù) */
return ret;
}
///////////////////////////// 以上為邏輯功能實(shí)現(xiàn) ////////////////////////////////////////
// 初始化函數(shù),需要識(shí)別并初始化硬件設(shè)備 這里對(duì)硬件初始化主要是調(diào)用各自的JNI接口初始化的
jint HardwareConfig_Init(JNIEnv *env, jobject thiz)
{
// 讀取配置文件
/* 功放初始化 */
Amp_Init();
Amp_Cmd(...);
Amp_Cmd(...);
Amp_Cmd(...);
/* 初始化麥克風(fēng) */
Mic_Init();
Mic_Cmd(...);
Mic_Cmd(...);
/* 初始化其他硬件 */
return 0;
}
///////////////////////////// JNI ////////////////////////////////////////
// 構(gòu)建映射表
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 文件,該文件是用來(lái)集中加載所有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;
}
//-------------------------------------------------------------------------------------
修改 對(duì)應(yīng)目錄下的 Android.mk 文件,該文件是用來(lái)編譯這些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)
/*************** 完成以上步驟就可以在當(dāng)前目錄下通過(guò) mm 命令編譯出 JNI SO庫(kù)文件了 ***********/
/////////////////////////////////////////////////////////////////////////////////////////////
Server:
在 android\frameworks\base\services\java\com\android\server 建立一個(gè)文件 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;
///////////////////////////////////// 初始化處理部分 ////////////////////////////////
// 當(dāng)這個(gè)類被創(chuàng)建時(shí),以下代碼將會(huì)被執(zhí)行
public HardwareConfigService(Context context)
{
mContext = context;
HardwareConfig_Init_native(); // <---- 這里調(diào)用 JNI 部分接口 進(jìn)行初始化
}
///////////////////////////////////// 接口定義部分 ////////////////////////////////
public int Amp_Cmd(int Cmd, int arg1) // <<---- 對(duì)外提供的調(diào)用的接口
{
int ret = 0;
// 在這里可以對(duì)接口做進(jìn)一步封裝
ret = Amp_Cmd_native(Cmd, arg1); // <---- 這里調(diào)用 JNI 部分接口
return ret;
}
public int Mic_Cmd(int Cmd, int arg1) // <<---- 對(duì)外提供的調(diào)用的接口
{
int ret = 0;
// 在這里可以對(duì)接口做進(jìn)一步封裝
ret = Mic_Cmd_native(Cmd, arg1); // <---- 這里調(diào)用 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 了一個(gè)對(duì)象并添加到 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 下通過(guò) mm 命令編譯了 ***********/
/////////////////////////////////////////////////////////////////////////////////////////////
AIAL:在 frameworks/base/core/java/android/os/ 新建立一個(gè)文件 IHardwareConfigService.aidl
/////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------
加入以下內(nèi)容,以下內(nèi)容為 服務(wù)里面的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
/////////////////////////////////////////////////////////////////////////////////////////////
接口文件:這個(gè)接口文件主要是進(jìn)一步封裝接口,把一些命令標(biāo)識(shí)什么的都放到問(wèn)個(gè)文件里面
在 android\frameworks\base\policy\src\com\android\internal\policy\impl
新建立一個(gè)文件 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";
///////////////////////// 功放命令與參數(shù)定義 ////////////////////////////
public enum enAmp_CMDTYPE
{
// 設(shè)置命令
eAMP_CMD_OPEN, // 打開(kāi)
eAMP_CMD_CLOSE, // 關(guān)閉
eAMP_CMD_SETMUTE, // 靜音
eAMP_CMD_SETVOLUME, // 設(shè)置音量
eAMP_CMD_SETEQ, // 設(shè)置音效
eAMP_CMD_SETSIGLEEND, // 設(shè)置立體聲
eAMP_CMD_SETSURROUND, // 設(shè)置環(huán)繞
eAMP_CMD_SETTREBLEBASS_CTRL, // 高低音控制
eAMP_CMD_SETTREBLE, // 設(shè)置高音
eAMP_CMD_SETBASS, // 設(shè)置低音
// .........
eAMP_CMD_ERROR, // 命令執(zhí)行錯(cuò)誤或失敗,同時(shí)也是命令的個(gè)數(shù)
};
///////////////////////// 功放接口 ////////////////////////////
/* 獲取某些命令是已經(jīng)使能了還是關(guān)閉了 設(shè)計(jì)此接口是為了應(yīng)用層更加方便的判斷 出錯(cuò)返回 eAMP_ARGS_NOT_USER*/
public enAmp_ARGSTYPE Amp_GetCmd(enAmp_CMDTYPE Cmd)
{
int index = 0;
// 省略轉(zhuǎn)換代碼
index = Amp_Cmd(Cmd, enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER.ordinal());
// 省略轉(zhuǎn)換代碼
return enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER;
}
/* 獲取具體的值 例如 高低音的值 出錯(cuò)返回 -1 */
public int Amp_GetData(enAmp_CMDTYPE Cmd)
{
int ret = Amp_Cmd(Cmd, enAmp_ARGSTYPE.eAMP_ARGS_NOT_USER.ordinal());
// 省略轉(zhuǎn)換代碼
return ret;
}
public enAmp_CMDTYPE Amp_SetCmd(enAmp_CMDTYPE Cmd, enAmp_ARGSTYPE Args)
{
int index = 0;
// 省略轉(zhuǎn)換代碼
index = Amp_Cmd(Cmd, Args.ordinal());
// 省略轉(zhuǎn)換代碼
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);
}
/* 最終的實(shí)現(xiàn)是由此命令完成的 設(shè)置命令時(shí)出錯(cuò)返回 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());
}
}
///////////////////////// 麥克風(fēng)接口與定義 ////////////////////////////
/* 功放命令集,具體命令需由具體的功放芯片驅(qū)動(dòng)實(shí)現(xiàn) */
public enum enMic_CMDTYPE
{
// 設(shè)置命令
eMIC_CMD_START , // 起始
eMIC_CMD_SETVOLUME, // 設(shè)置MIC音量
eMIC_CMD_SETECHO, // 設(shè)置MIC回響
eMIC_CMD_SETMUTE, // 麥克風(fēng)靜音
// 獲取命令
eMIC_CMD_GETTYPE_START, // 獲取命令類型 用于做標(biāo)識(shí) 當(dāng)大于此值時(shí)表示是獲取命令類型
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, // 命令執(zhí)行錯(cuò)誤或失敗,同時(shí)也是命令的個(gè)數(shù)
};
public enum enMic_ARGSTYPE // 參數(shù)
{
eMIC_ARGS_DISABLE ,
eMIC_ARGS_ENABLE,
eMIC_ARGS_NOT_USER, // 不使用
};
///////////////////////// 麥克風(fēng)接口實(shí)現(xiàn) ////////////////////////////
/* 獲取某些命令是已經(jīng)使能了還是關(guān)閉了 設(shè)計(jì)此接口是為了應(yīng)用層更加方便的判斷 出錯(cuò)返回 eMIC_ARGS_NOT_USER*/
public enMic_ARGSTYPE Mic_GetCmd(enMic_CMDTYPE Cmd)
{
int index = 0;
// 省略轉(zhuǎn)換代碼
index = Mic_Cmd(Cmd, enMic_ARGSTYPE.eMIC_ARGS_NOT_USER.ordinal());
// 省略轉(zhuǎn)換代碼
return enMic_ARGSTYPE.eMIC_ARGS_NOT_USER;
}
/* 獲取具體的值 出錯(cuò)返回 -1 */
public int Mic_GetData(enMic_CMDTYPE Cmd)
{
int ret = Mic_Cmd(Cmd, enMic_ARGSTYPE.eMIC_ARGS_NOT_USER.ordinal());
// 省略轉(zhuǎn)換代碼
return ret;
}
public enMic_CMDTYPE Mic_SetCmd(enMic_CMDTYPE Cmd, enMic_ARGSTYPE Args)
{
int index = 0;
// 省略轉(zhuǎn)換代碼
index = Mic_Cmd(Cmd, Args.ordinal());
// 省略轉(zhuǎn)換代碼
return enMic_CMDTYPE.eMIC_CMD_ERROR;
}
public int Mic_SetData(enMic_CMDTYPE Cmd, int Args)
{
// 省略轉(zhuǎn)換代碼
return Mic_Cmd(Cmd, Args);
}
/* 最終的實(shí)現(xiàn)是由此命令完成的 設(shè)置命令時(shí)出錯(cuò)返回 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 建議全編一次
*****************************************************************************/
/////////////////////////////////////////////////////////////////////////////////////////////
應(yīng)用層使用方法:
/////////////////////////////////////////////////////////////////////////////////////////////
1、需要在 Android 工程中加上 android\out\target\common\obj\JAVA_LIBRARIES\android.policy_intermediates\classes.jar
2、導(dǎo)入需要的數(shù)據(jù)結(jié)構(gòu)
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、在初始化函數(shù)中new 對(duì)象
HardwareConfig = new HardwareConfigServiceInterface();
5、在需要的地方直接調(diào)用接口
HardwareConfig.Amp_GetCmd(enAmp_CMDTYPE.eAMP_CMD_GETSDMUTE)
|
|