22-08-04 Android 开机开启一个系统服务SystemService,里面含有Handler handleMessage、发送和接收Broadcast、Thread、静态注册jni 实例

前言:这个实例源于读写serial串口设备数据的需求,开机后开启一个系统服务SystemService,里面含有Handler handleMessage、发送和接收Broadcast、Thread、静态注册jni 实例,对理解service和jni有所帮助。

一、添加 SystemService文件,里面含有Handler handleMessage、发送和接收Broadcast、Thread、jni静态注册和service连接frameworks\base\services\core\java\com\android\server\power\GiadaService.java

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.power;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.OsProtoEnums;
import android.os.PowerManager;
import android.util.Slog;
import android.util.Log;

import com.android.internal.os.CachedDeviceState;
import com.android.server.SystemService;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.IOException;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.*;
import android.net.wifi.WifiManager;
import android.os.SystemProperties;
import android.os.Handler;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Looper;
import android.os.Message;
import android.annotation.NonNull;


public final class GiadaService extends SystemService {

    private static final String TAG = "GiadaService";

	private static final int TIME_MESSAGE = 100;
	private ReadThread mReadThread;
	int timeCount = 0;

	private Handler timeHandler = new Handler() {
			@Override
			public void handleMessage(@NonNull Message msg) {
				super.handleMessage(msg);
				switch (msg.what) {
					case TIME_MESSAGE:
				          final int number = (int) msg.obj;
						  Slog.d(TAG, "Received message-TIME_MESSAGE number="+number);
					      if(number == 100){
				             Intent tempwarning = new Intent("PNW.clickedTempWarning");
                             getContext().sendBroadcast(tempwarning);
					      }
						break;
					default:
						break;
				}
			}
	};

	private class ReadThread extends Thread {
		@Override
		public void run() {
			super.run();
			while(!isInterrupted()) {
				  try {
						Thread.sleep(1000);
						timeCount++;
						Message msg = timeHandler.obtainMessage(TIME_MESSAGE, timeCount);
						msg.setAsynchronous(true);
						msg.sendToTarget();
						Slog.d(TAG, "obtainMessage sendToTarget,timeCount:" + timeCount);
					   } catch (InterruptedException e) {
						 e.printStackTrace();
						 break;
					 }
				  }
			}
	}

    //final EthernetServiceImpl mImpl;
	private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
		 @Override
		 public void onReceive(Context context, Intent intent) {
			 Slog.wtf(TAG, "BroadcastReceiver onReceive action:"+intent.getAction());
		     switch (intent.getAction()) {
				 case "android.intent.giada.sendbroadcast":
	                     Intent tempwarning = new Intent("PNW.clickedTempWarning");
                         getContext().sendBroadcast(tempwarning);
					 break;
				 case "android.intent.giada.stopthread":
					     if (mReadThread != null)
			              mReadThread.interrupt();
						  Slog.wtf(TAG, "BroadcastReceiver getString():"+getString());
						  Slog.wtf(TAG, "BroadcastReceiver setString():"+setString("SHIT"));
					 break;
		 	  }
 
		 }
	 };
	
    public GiadaService(Context context) {
        super(context);
		Log.i(TAG, "GiadaService context!" );
        //mImpl = new EthernetServiceImpl(context);
    }

    @Override
    public void onStart() {
        Log.i(TAG, "onStart,creat thread!" );
		mReadThread = new ReadThread();
		mReadThread.start();
			
        //publishBinderService(Context.ETHERNET_SERVICE, mImpl);
    }

    @Override
    public void onBootPhase(int phase) {
        Log.i(TAG, "onBootPhase" );
	    if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY){
	        Log.i(TAG, "onBootPhase PHASE_SYSTEM_SERVICES_READY" );
			final IntentFilter filter = new IntentFilter();
	        filter.addAction("android.intent.giada.sendbroadcast");
			filter.addAction("android.intent.giada.stopthread");
	        getContext().registerReceiver(mBroadcastReceiver, filter);
	    }
    }

	public static native String getString();
    public static native String setString(String str);
}

二、开启服务,在frameworks\base\services\java\com\android\server\SystemServer.java startBootstrapServices函数里面添加下面内容开机服务

t.traceBegin("StartGiadaService");
mSystemServiceManager.startService(GiadaService.class);
t.traceEnd();

 三、JNI 部分,在 frameworks/base/services/core/jni/下面添加 两个文件com_android_server_power_GiadaService.c和com_android_server_power_GiadaService.h

       3.1、com_android_server_power_GiadaService.c

/*
 * Copyright 2009-2011 Cedric Priscal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>


#include "android/log.h"
#include "com_android_server_power_GiadaService.h"

static const char *TAG="SERIAL-PORT_JNI";
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)


JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_getString(JNIEnv *env, jclass cls)
{
    LOGD("Java_com_android_server_power_GiadaService_getString\r\n");
    return (*env)->NewStringUTF(env, "getString,return hello from jni");
}

JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_setString(JNIEnv *env, jclass cls, jstring str)
{
     const char *g_str = (*env)->GetStringUTFChars(env,str, NULL);
     LOGD("Java_com_android_server_power_GiadaService_setString jni string=%s \r\n",g_str);
	 if (g_str != NULL) {
        (*env)->ReleaseStringUTFChars(env, str, g_str);
     }
     return (*env)->NewStringUTF(env, "setString,return hi from jni");
}

       3.2、com_android_server_power_GiadaService.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_android_server_power_GiadaService */

#ifndef _Included_com_android_server_power_GiadaService
#define _Included_com_android_server_power_GiadaService
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_android_server_power_GiadaService
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_getString
  (JNIEnv *, jclass);



/*
 * Class:     com_android_server_power_GiadaService
 * Method:    setString
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_setString
  (JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif

3、在 frameworks/base/services/core/jni/Android.mk 里面添加"com_android_server_power_GiadaService.c" 去编译编译com_android_server_power_GiadaService.c

4、这里jni中的写法是

JNIEXPORT jstring JNICALL Java_com_android_server_power_GiadaService_getString
  (JNIEnv *, jclass);这种写法不需要再手动注册native方法,如果是其他写法,需要在.c中添加手动注册方法,并在onload.cpp中注册。

四、烧录真机测试,通过adb shell am broadcast -a "android.intent.giada.sendbroadcast"命令发从广播,服务里面接受到该广播后再发一个广播调出提示窗口;通过adb shell am broadcast -a "android.intent.giada.stopthread" 发送一个广播,服务里面接收到该广播后会通过interrupt退出线程,并且调用jni接口getString和setString,通过log上看有SERIAL-PORT_JNI的相关打印,说明已经成功调用到jni接口,这种事静态注册jni,相对动态注册jni比较简单。

 

五、整个过程中涉及到的文件

六、非常有价值的参考文章

1、SystemService与jni的静态注册连接,用于串口通信

android 系统添加jni,注册本地方法_王不六的博客-CSDN博客_android 系统服务jni

2、SystemService里面publishBinderService的栗子

android 10 添加系统服务步骤_青春给了狗的博客-CSDN博客_android 系统服务

1.编写.aidl文件
存放位置:frameworks/base/core/java/android/os

package android.os;
interface ITEST {   
    //此处注意 如果参数中有数组类型记得加入out 和in关键字
    // int test(in byte【】 a)
    void test();
}

---------------------------------------------------------------------------------------------------------------------------------
2、将.aidl文件添加到 frameworks/base/Android.mk下的 LOCAL_SRC_FILES
(此处先make update-api生成对应的文件 Frameworks/base/api/current.txt中查看是否存在对应ITEST接口)
注意:Android 9以上是添加到frameworks/base/ Android.bp
存放位置:framework/base

java_defaults {
    name: "framework-defaults",
    installable: true,
    srcs: [
         ...省略
        "core/java/android/os/ITEST.aidl",
        ...省略

---------------------------------------------------------------------------------------------------------------------------------
3、Context.java添加服务注册名称, 添加该服务名称, 用于快捷注册和快捷引用
修改位置:frameworks/base/core/java/android/content/

  /**
     * Use with {@link #getSystemService(String)} to retrieve a
     * {@link android.app.TestManager} for controlling power management,
     * including "wake locks," which let you keep the device on while
     * you're running long tasks.
     */
 public static final String ITEST_SERVICE = "itest";

/** @hide */
    @StringDef(suffix = { "_SERVICE" }, value = {
            POWER_SERVICE,
            ITEST_SERVICE 
       ....,}

---------------------------------------------------------------------------------------------------------------------------------

4、新建TestService.java和TestManager.java
存放位置:frameworks\base\services\core\java\com\android\server\TestService.java
frameworks\base\core\java\android\app\TestManager.java

//TestManager 类
package android.app;
import android.content.Context;
import android.os.IFan;
import android.os.RemoteException;
import android.annotation.SystemService;

@SystemService(Context.ITEST_SERVICE )
public class TestManager {
    public static final String TAG = "TestManager";

    private Context mContext;
    private TestService mService;

    public FanManager(Context mContext, TestService mService) {
        this.mContext = mContext;
        this.mService = mService;
    }
    pubic void test(){
       try {
               mService.test();
             } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            }
    }
}


//TestService 类
package com.android.server;
import android.app.TestManager;
import android.content.Context;
import android.os.ITEST;
import android.os.RemoteException;
import com.android.server.SystemService;
import com.android.internal.app.IAppOpsService;

public class TestService extends SystemService {
    private Context mContext;
    private IAppOpsService mAppOps;
    private TestManager mManager;
    public TestService(Context context) {
        super(context);
        this.mContext = context;
    }
    public void systemReady(IAppOpsService appOps) {
      //TODO
    }
    @Override
    public void onStart() {
        publishBinderService(Context.ITEST_SERVICE, new BinderService());
    }
    private final class BinderService extends ITEST.Stub {
        @Override
        public void test() throws RemoteException {
           //TODO 具体操作在这里面实现
        }
    }
}

---------------------------------------------------------------------------------------------------------------------------------

5、SystemServer.java 中注册该service
修改位置: frameworks\base\services\java\com\android\server

import com.android.server.TestService ;//导包

private TestService  mTestService; //定义

 在SystemServer.java 中的startBootstrapServices()方法添加

 traceBeginAndSlog("StartTestManager");
 mTestService= mSystemServiceManager.startService(TestService.class);
 traceEnd();

traceBeginAndSlog("MakeTestManagerServiceReady");
 try {
            // TODO: use boot phase
  mTestService.systemReady(mActivityManagerService.getAppOpsService());
      } catch (Throwable e) {
   reportWtf("making Test Manager Service ready", e);
      }
 traceEnd();

---------------------------------------------------------------------------------------------------------------------------------

6、SystemServiceRegistry的static{}, 并在其中注册该service
修改位置:frameworks\base\core\java\android\app
import android.os.ITEST;//导包
//TestManager在同一目录下所以不用导包

   registerService(Context.ITEST_SERVICE, TestManager.class,ew CachedServiceFetcher() {
     @Override
     public I2CManager createService(ContextImpl ctx)throws ServiceNotFoundException {
                        IBinder b = ServiceManager.getServiceOrThrow(            Context.ITEST_SERVICE);
                        return new TestManager(
                                ctx.getOuterContext(),ITEST.Stub.asInterface(b));
        }});


以上步骤完成我们的自定义系统服务就完成了90%   但是我们还有最后一步,也是最重要的一步:

---------------------------------------------------------------------------------------------------------------------------------

7、 设置selinux规则
然后我们只需要添加这个自定义服务TestService相关的 SELinux 规则。为了方便之后验证,打开selinux
要记住这个命令  adb shell setenforce 1   # 1为打开  #0为关闭
Android 10 的 selinux 规则是放在 system/sepolicy 目录下的:
 service.te 和 service_contexts 都要加上 TestService的配置:

./prebuilts/api/27.0/public/service.te   # 需要
./prebuilts/api/28.0/public/service.te   # 需要
./prebuilts/api/29.0/public/service.te    # 需要
./prebuilts/api/26.0/public/service.te   # 需要
./public/service.te                 # 需要


./prebuilts/api/27.0/private/service_contexts   # 需要
./prebuilts/api/28.0/private/service_contexts   # 需要
./prebuilts/api/29.0/private/service_contexts       # 需要
./prebuilts/api/26.0/private/service_contexts   # 需要
./private/service_contexts         # 需要


在上述文件中
service_contexts文件添加配置 itest   u:object_r:itest_service:s0
service.te  中添加type  itest_service , system_server_service, service_manager_type
总结出一个规律 :就是service.te 文件在public目录下需要添加,private目录下不需要
                           service_contexts文件在private目录下的需要添加,public目录不需要
到此系统服务已经添加完成
itest                                    u:object_r:itest_service:s0

---------------------------------------------------------------------------------------------------------------------------------
8、验证:
    编译完成后,输入adb shell
                           #service  list 
    查看服务列表中 是否存在有添加服务 :itest        
    如果不存在 逐步排查 参照上一步看哪一步错误
    如果存在 就在代码中验证
    找到编译最新生成的class.jar文件,导入Androidstudio(如何导入自行百度)
   编译后的class.jar目录\out\target\common\obj\JAVA_LIBRARIES\framework_intermediates

  如果未生成  执行下make javac-check-framework 这个命令 就会生成!!!

觉得我写的好的兄弟 动动你发财的小手 点个赞 !!!

你们的认同将是我继续写下去的动力 !!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的 Android Studio 代码示例,用于注册一个服务并在每5秒钟执行一次任务,更新UI以显示任务的进度和结果: 1. 在 Android Studio 中创建一个新项目,并创建一个新的服务类(例如 MyService.java)。 2. 在 MyService 类中添加以下代码: ```java public class MyService extends Service { private Handler handler; private Runnable runnable; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { handler = new Handler(); runnable = new Runnable() { int count = 0; @Override public void run() { count++; updateUI("Task running: " + count); if (count == 10) { updateUI("Task completed"); stopSelf(); } else { handler.postDelayed(runnable, 5000); } } }; handler.post(runnable); return START_STICKY; } private void updateUI(String message) { Intent intent = new Intent("my-event"); intent.putExtra("message", message); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } } ``` 3. 在 MainActivity 类中,添加以下代码来启动 MyService 并更新UI: ```java public class MainActivity extends AppCompatActivity { private TextView textView; private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String message = intent.getStringExtra("message"); textView.setText(message); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.text_view); Intent intent = new Intent(this, MyService.class); startService(intent); LocalBroadcastManager.getInstance(this).registerReceiver(receiver, new IntentFilter("my-event")); } @Override protected void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); } } ``` 4. 最后,在 activity_main.xml 中添加一个 TextView 来显示任务的进度和结果: ```xml <TextView android:id="@+id/text_view" android:layout_width="match_parent" android:layout_height="match_parent" android:text="Task not started" android:textSize="24sp"/> ``` 这样,你就可以在 Android Studio注册一个服务,并在每5秒钟执行一次任务,并更新UI显示任务的进度和结果。完整代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值