本文以mtk android5.1为研究对象。
Android的系统服务都是托管给ServiceManager管理的,我们可以自定义一个自己的服务,并将其添加到ServiceManager中,本文以添加一个Tts语音服务为例,供应用层调用。
1、首先,在frameworks/base/core/Java/android/app中新建一个aidl文件 ITtsManager.aidl,内容如下:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package android.app;
interface ITtsManager {
int speak(String message, String from);
int stop();
}
2、将此aidl文件加入编译列表frameworks/base/Android.mk:
[java] view plain copy 在CODE上查看代码片派生到我的代码片
LOCAL_SRC_FILES += \
....
core/java/android/app/ITtsManager.aidl \
....
3、在frameworks/base/services/core/java/com/android/server/tts路径下(路径可以自己改变)新建一个TtsManagerService.java类,该类继承了TtsManager.Stub,
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.android.server.tts;
import android.content.Context;
import android.speech.tts.TtsEngines;
import android.speech.tts.TextToSpeech;
import android.os.Handler;
import android.util.Log;
import android.app.ITtsManager;
public final class TtsManagerService extends ITtsManager.Stub {
private static final String TAG = "TtsManagerService";
final Context mContext;
private final TextToSpeech mTts;
public TtsManagerService(Context context) {
Log.d(TAG, "Tts init complete");
}
@Override
public int speak(String message, String from) {
//TODO:
return 0;
}
@Override
public int stop() { //TODO:
return 0;
}}
4、还得在frameworks/base/core/java/android/app创建一个管理器类TtsManager.java,
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package android.os;
import android.content.Context;
public class TtsManager {
final ITtsManager mService;
final Context mContext;
public TtsManager(Context context, ITtsManager service) {
mContext = context;
mService = service;
}
public int speak(String message, String from) {
try {
return mService.speak(message, from);
} catch (RemoteException e) {
e.printStackTrace();
return -1;
}
}
public int stop() {
try {
return mService.stop();
} catch (RemoteException e) {
e.printStackTrace();
return -1;
}
}
}
5、为了让应用层可以使用getSystemService接口获得服务,现在需要在ContextImpl.java的static代码块将TtsManager创建起来,
[java] view plain copy 在CODE上查看代码片派生到我的代码片
/// M: comment @{ add TtsManagerService by Lee
if ("1".equals(SystemProperties.get("ro.qxt.tts.support"))) {
registerService("qxttts", new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService("qxttts"); //注意:qxttts 是服务名称
return new TtsManager(ctx, ITtsManager.Stub.asInterface(b));
} });
}
/// @}
6、现在可以将这个服务加入到ServiceManager中了,
[java] view plain copy 在CODE上查看代码片派生到我的代码片
try {
Slog.i(TAG, "Tts Manager");
ServiceManager.addService("qxttts", new TtsManagerService(context)); //服务名称qxttts与上面对应
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Tts Manager", e);
}
7、然后你可以在某个app的activity上获得这个服务,
[java] view plain copy 在CODE上查看代码片派生到我的代码片
((TtsManager)getSystemService("qxttts")).speak("测试用语", "activity");
编译(先用update-api命令更新sdk),将相应模块push到手机上。。。
现在你可能会出现app启动报错的提示。。错误类似于speak方法处出现空指针,这是服务没起来,出现类似如下错误,
[java] view plain copy 在CODE上查看代码片派生到我的代码片
ServiceManager( 232): add_service('qxttts',45) uid=1000 - PERMISSION DENIED
这是因为SELinux Policy限制,新加一个系统服务,你得先在device/mediatek/common/sepolicy/service_contexts注册你的服务,
[plain] view plain copy 在CODE上查看代码片派生到我的代码片
#Lee add begin
qxttts u:object_r:system_server_service:s0
#Lee add end
此处是vendor自己加的文件,android原生的服务在external/sepolicy/service_contexts中注册,external/sepolicy/service.te定义了各种类型如system_server_service。
重新编译,可以验证了。。。
未完待续,有不对的地方,请指正。
本人修复:
service_contexts:中
qxttts u:object_r:qxttts_service:s0
service.te中:
type qxttts_service, app_api_service, system_server_service, service_manager_type;