由于项目需要,我这边需要在framework层增加一个语音唤醒服务来实现拉通hal层以及android应用层,实现语音黑屏唤醒以及亮屏唤醒的功能,这里我们只看在android7.0和9.0上是怎么增加一个自定义系统服务,其实对着葫芦画瓢就可以了。
下面主要说基于android7.0的添加系统服务,9.0的编译方式改了,改下bp文件就可以了,没什么大的区别,这里删了些复杂的接口,只留了几个比较简单的接口,方便阅读。
在frameworks/base/core/java/android/新增ovum/ai/IAIManager.aidl文件
package android.ovum.ai;
/**
* {@hide}
*/
interface IAIManager {
byte[] getAudioData();
int getWakeupDoa();
void stopAudioData();
void wakeupAppStarted(String callerPackage);
boolean setEnabled(boolean enabled, String callerPackage);
boolean isEnabled(String callerPackage);
boolean isRecording(String callerPackage);
}
实现完成后需要在frameworks/base/Android.mk 声明我们需要的aidl文件,这个经常忘记了。
core/java/android/ovum/ai/IAIManager.aidl \
core/java/android/ovum/performanceobserver/IPerformanceObserverService.aidl \
实现AIManager的接口封装
package android.ovum.ai;
import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
/**
* {@hide}
*/
public class AIManager {
private static final String TAG = "AIManager";
private final Context mContext;
private final IAIManager mService;
public AIManager(Context context, IAIManager service) {
Log.v(TAG, "AIManager()");
mContext = context;
mService = service;
}
public void stopAudioData() {
try {
mService.stopAudioData();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public int getWakeupDoa() {
try {
return mService.getWakeupDoa();
} catch (RemoteException e) {
e.printStackTrace();
}
return 0;
}
public byte[] getAudioData() {
try {
return mService.getAudioData();
} catch (RemoteException e) {
e.printStackTrace();
}
return null;
}
public void wakeupAppStarted() {
try {
mService.wakeupAppStarted(mContext.getOpPackageName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
public boolean setEnabled(boolean enabled) {
try {
return mService.setEnabled(enabled, mContext.getOpPackageName());
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
}
public boolean isEnabled() {
try {
return mService.isEnabled(mContext.getOpPackageName());
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
}
public boolean isRecording() {
try {
return mService.isRecording(mContext.getOpPackageName());
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
}
}
package com.android.server.ovum;
import android.content.Context;
import android.media.AudioFormat;
import android.os.RemoteException;
import android.ovum.ai.IAIManager;
import android.util.Slog;
import android.media.MediaRecorder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PowerManager;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.app.ActivityManagerInternal;
import com.android.server.LocalServices;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import java.util.concurrent.ArrayBlockingQueue;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.File;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Parcelable;
import android.content.BroadcastReceiver;
import android.provider.Settings;
public class AIManagerService extends IAIManager.Stub {
private static final String TAG = "AIManagerService";
private Context mContext;
private static boolean LOG_DEBUG = true;
private boolean mIsEnabled = true;
public AIManagerService(final Context context) {
Slog.d(TAG, "AIManagerService()");
mContext = context;
}
public void handleOnBootPhase() {
Slog.d(TAG, "handleOnBootPhase");
}
@Override
public void stopAudioData() throws RemoteException {
Slog.d(TAG, "stopAudioData!\n");
}
@Override
public int getWakeupDoa() throws RemoteException {
return 0;
}
@Override
public byte[] getAudioData() throws RemoteException {
return null;
}
@Override
public void wakeupAppStarted(String callerPackage) throws RemoteException {
Slog.d(TAG, "wakeupAppStarted caller=" + callerPackage);
return;
}
@Override
public boolean setEnabled(boolean enabled, String callerPackage) throws RemoteException {
Slog.d(TAG, "setEnabled=" + enabled + ",caller=" + callerPackage);
return false;
}
@Override
public boolean isEnabled(String callerPackage) throws RemoteException {
Slog.d(TAG, "isEnabled=" + mIsEnabled + ",caller=" + callerPackage);
return mIsEnabled;
}
@Override
public boolean isRecording(String callerPackage) throws RemoteException {
return false;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump ovum_ai from from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " without permission " + android.Manifest.permission.DUMP);
return;
}
pw.println("AIManagerService");
}
}
这里我们为了方便统一管理我们增加的自定义系统服务
在frameworks/base/services/core/java/com/android/server/目录下新增ovum/OvumOvulationService.java ,实现OvumOvulationService 并继承SystemService
package com.android.server.ovum;
import android.content.Context;
import android.util.Slog;
import com.android.server.am.ActivityManagerService;
import com.android.server.SystemService;
public class OvumOvulationService extends SystemService {
private final static String TAG = "OvumOvulationService";
private Context mContext;
private AIManagerService mAiManagerService;
public OvumOvulationService(Context context) {
super(context);
mContext = context;
mAiManagerService = new AIManagerService(context);
}
@Override
public void onStart() {
Slog.d(TAG, "onStart ------");
}
@Override
public void onBootPhase(int phase) {
Slog.d(TAG, "onBootPhase ------ " + phase);
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
publishBinderService(Context.OVUM_AI_SERVICE, mAiManagerService);
mAiManagerService.handleOnBootPhase();
}
}
}
修改frameworks/base/services/java/com/android/server/SystemServer.java 实现启动我们的服务
import com.android.server.ovum.OvumOvulationService;
private OvumOvulationService mOvumOvulationService;
...
mSystemServiceManager.startService(OvumOvulationService.class);
同时修改frameworks/base/core/java/android/content/Context.java 新增 OVUM_AI_SERVICE,方便dump信息查看
/**
* @hide
*/
public static final String OVUM_AI_SERVICE = "ovum_ai";
修改system/sepolicy/service_contexts文件,修改权限
ovum_ai u:object_r:ovum_service:s0
这样整个系统服务就声明和实现完成了。编译替换后,重启系统我们就可以看到system log了
01-11 01:53:29.183742 708 708 D AIManagerService: AIManagerService()
01-11 01:53:29.184836 708 708 D OvumOvulationService: onStart ------
01-01 00:00:01.628209 708 748 D OvumOvulationService: onBootPhase ------ 1000
01-01 00:00:01.628901 708 748 D AIManagerService: handleOnBootPhase
说明我们的服务正常起来了
这里我们不做注册监听系统服务这些操作,直接通过反射并封装一个jar包提供给应用使用,这里直接使用反射的方式。
public class AIManager {
private static final String TAG = "AIManager.jar";
private static final boolean DEBUG = true;
private static AIManager sAIManager;
private static Object mSysAIManagerObject;
private static Class mSysAIManagerClass;
private static Method mMethodSwitchToWakeupMode;
private static Method mMethodSwitchToRecordMode;
private static Method mMethodWakeupAppStarted;
private static Method mMethodOneshotRecordStarted0;
private static Method mMethodOneshotRecordStarted;
private static Method mMethodSetEnabled;
private static Method mMethodIsEnabled;
private static Method mMethodIsRecording;
private static Method mMethodCallback;
private static Method mMethodUnCallback;
private static Method mMethodgetAudioData;
private static Method mMethodgetWakeupDoa;
private static Method mMethodstopAudiodata;
private static Method mMethodauthDuiTest;
public AIManager(Context context) {
if (DEBUG) {
Log.d(TAG, "new AIManager from package " + context.getPackageName());
}
}
public static AIManager getInstance(Context context) {
if (sAIManager == null) {
sAIManager = new AIManager(context);
mSysAIManagerObject = context.getApplicationContext().getSystemService("ovum_ai");
if (DEBUG) {
Log.d(TAG, "mSysAIManagerObject=" + mSysAIManagerObject);
}
if (mSysAIManagerObject != null) {
mSysAIManagerClass = mSysAIManagerObject.getClass();
}
try {
mMethodSetEnabled = mSysAIManagerClass.getMethod("setEnabled", boolean.class);
mMethodSetEnabled.setAccessible(true);
} catch (NoSuchMethodException e) {
Log.e(TAG, e.getMessage());
}
try {
mMethodIsEnabled = mSysAIManagerClass.getMethod("isEnabled");
mMethodIsEnabled.setAccessible(true);
} catch (NoSuchMethodException e) {
Log.e(TAG, e.getMessage());
}
try {
mMethodIsRecording = mSysAIManagerClass.getMethod("isRecording");
mMethodIsRecording.setAccessible(true);
} catch (NoSuchMethodException e) {
Log.e(TAG, e.getMessage());
}
try {
mMethodgetAudioData = mSysAIManagerClass.getMethod("getAudioData");
mMethodgetAudioData.setAccessible(true);
} catch (NoSuchMethodException e) {
Log.e(TAG, e.getMessage());
}
try {
mMethodgetWakeupDoa = mSysAIManagerClass.getMethod("getWakeupDoa");
mMethodgetWakeupDoa.setAccessible(true);
} catch (NoSuchMethodException e) {
Log.e(TAG, e.getMessage());
}
try {
mMethodstopAudiodata = mSysAIManagerClass.getMethod("stopAudioData");
mMethodstopAudiodata.setAccessible(true);
} catch (NoSuchMethodException e) {
Log.e(TAG, e.getMessage());
}
}
return sAIManager;
}
public void stopAudiodata() {
if (sAIManager == null || mMethodstopAudiodata == null || mSysAIManagerObject == null)
return;
try {
mMethodstopAudiodata.invoke(mSysAIManagerObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public byte[] getAudioData() {
if (sAIManager == null || mMethodgetAudioData == null || mSysAIManagerObject == null)
return null;
try {
return (byte[]) mMethodgetAudioData.invoke(mSysAIManagerObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public int getWakeupDoa() {
if (sAIManager == null) return -1;
try {
return (int) mMethodgetWakeupDoa.invoke(mSysAIManagerObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return -1;
}
public void wakeupAppStarted() {
if (sAIManager == null) return;
try {
mMethodWakeupAppStarted.invoke(mSysAIManagerObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public boolean setEnabled(boolean enabled) {
if (sAIManager == null) return false;
try {
return (boolean) mMethodSetEnabled.invoke(mSysAIManagerObject, enabled);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
public boolean isEnabled() {
if (sAIManager == null) return false;
try {
return (boolean) mMethodIsEnabled.invoke(mSysAIManagerObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
public boolean isRecording() {
if (sAIManager == null) return false;
try {
return (boolean) mMethodIsRecording.invoke(mSysAIManagerObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
}
应用只需要集成jar包,然后如下这样调用就可以了
AIManager.getInstance(MainActivity.this).setEnabled(true);