From 4a984f16098f56653d5bd7b9de4e20f57b32449c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A7=9C=E5=A4=A9=E5=AE=87?= <jiangtianyu@wmdigit.com>
Date: Tue, 24 Sep 2024 10:07:49 +0800
Subject: [PATCH] =?UTF-8?q?feat(v1.0.2):=20=E5=AE=8C=E5=96=84AIDL=E6=8E=A5?=
 =?UTF-8?q?=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/build.gradle                              |   4 +-
 app/src/main/AndroidManifest.xml              |   9 +-
 .../wmdigit/cateringdetect/Application.java   |   3 +
 .../wmdigit/common/constants/ErrorCode.java   |  26 ++
 .../com/wmdigit/common/model/ProductsDTO.java |  52 ++-
 core/src/main/cpp/videopipe_rk3568.cpp        |  18 +-
 .../java/com/wmdigit/core/CoreModule.java     |   5 +-
 .../com/wmdigit/core/hnsw/HnswRepository.java |  20 +
 ...eManager.java => VideoPipeRepository.java} |  12 +-
 .../data/database/dao/ProductsDao.java        |   8 +
 .../repository/ProductsRepository.java        |   8 +
 .../demo/ui/fragment/DemoHomeFragment.java    |   2 -
 .../viewmodel/DataLearningViewModel.java      |  13 +-
 service-sdk/.gitignore                        |   1 +
 service-sdk/build.gradle                      |  54 +++
 .../consumer-rules.pro                        |   0
 service-sdk/proguard-rules.pro                |  21 +
 .../service/ExampleInstrumentedTest.java      |  26 ++
 service-sdk/src/main/AndroidManifest.xml      |   4 +
 .../com/wmdigit/common/model/ProductsDTO.aidl |   3 +
 .../wmdigit/service/ICateringInterface.aidl   |  30 ++
 .../wmdigit/service/IOnDetectionListener.aidl |   7 +
 .../com/wmdigit/service/IOnInitListener.aidl  |   6 +
 .../service/aidl/model/DetectResult.aidl      |   2 +
 .../com/wmdigit/common/model/ProductsDTO.java | 138 +++++++
 .../main/java/com/wmdigit/service/WmSdk.java  | 297 ++++++++++++++
 .../com/wmdigit/service/WmSdkInterface.java   | 120 ++++++
 .../service/aidl/model/DetectResult.java      | 175 ++++++++
 .../listener/OnServiceConnectListener.java    |  26 ++
 .../com/wmdigit/service/ExampleUnitTest.java  |  17 +
 service/build.gradle                          |  11 +
 service/src/main/AndroidManifest.xml          |  18 +
 .../com/wmdigit/common/model/ProductsDTO.aidl |   3 +
 .../wmdigit/service/ICateringInterface.aidl   |   7 +-
 .../wmdigit/service/IOnDetectionListener.aidl |   2 +-
 .../com/wmdigit/service/ServiceModule.java    |  35 ++
 .../service/aidl/CateringInterfaceImpl.java   | 374 +++++++++++++++++-
 .../wmdigit/service/aidl/CateringService.java |  68 +++-
 .../service/aidl/model/DetectResult.java      |  77 +++-
 settings.gradle                               |   1 +
 40 files changed, 1638 insertions(+), 65 deletions(-)
 create mode 100644 common/src/main/java/com/wmdigit/common/constants/ErrorCode.java
 rename core/src/main/java/com/wmdigit/core/videopipe/{VideoPipeManager.java => VideoPipeRepository.java} (93%)
 create mode 100644 service-sdk/.gitignore
 create mode 100644 service-sdk/build.gradle
 rename service/src/main/java/com/wmdigit/service/aidl/model/CMakeLists.txt => service-sdk/consumer-rules.pro (100%)
 create mode 100644 service-sdk/proguard-rules.pro
 create mode 100644 service-sdk/src/androidTest/java/com/wmdigit/service/ExampleInstrumentedTest.java
 create mode 100644 service-sdk/src/main/AndroidManifest.xml
 create mode 100644 service-sdk/src/main/aidl/com/wmdigit/common/model/ProductsDTO.aidl
 create mode 100644 service-sdk/src/main/aidl/com/wmdigit/service/ICateringInterface.aidl
 create mode 100644 service-sdk/src/main/aidl/com/wmdigit/service/IOnDetectionListener.aidl
 create mode 100644 service-sdk/src/main/aidl/com/wmdigit/service/IOnInitListener.aidl
 create mode 100644 service-sdk/src/main/aidl/com/wmdigit/service/aidl/model/DetectResult.aidl
 create mode 100644 service-sdk/src/main/java/com/wmdigit/common/model/ProductsDTO.java
 create mode 100644 service-sdk/src/main/java/com/wmdigit/service/WmSdk.java
 create mode 100644 service-sdk/src/main/java/com/wmdigit/service/WmSdkInterface.java
 create mode 100644 service-sdk/src/main/java/com/wmdigit/service/aidl/model/DetectResult.java
 create mode 100644 service-sdk/src/main/java/com/wmdigit/service/listener/OnServiceConnectListener.java
 create mode 100644 service-sdk/src/test/java/com/wmdigit/service/ExampleUnitTest.java
 create mode 100644 service/src/main/aidl/com/wmdigit/common/model/ProductsDTO.aidl
 create mode 100644 service/src/main/java/com/wmdigit/service/ServiceModule.java

diff --git a/app/build.gradle b/app/build.gradle
index ea078b8..6072dcd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -59,8 +59,8 @@ android {
         //如果合并不能解决问题就选择其中一个
         merge 'META-INF/proguard/androidx-annotations.pro'
         merge 'META-INF/proguard/coroutines.pro'
-//        merge 'lib/arm64-v8a/libc++_shared.so'
-//        merge 'lib/armeabi-v7a/libc++_shared.so'
+        merge 'lib/arm64-v8a/libc++_shared.so'
+        merge 'lib/armeabi-v7a/libc++_shared.so'
         pickFirst 'lib/x86/libc++_shared.so'
         pickFirst 'lib/x86_64/libc++_shared.so'
         pickFirst 'lib/arm64-v8a/libc++_shared.so'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e9d315d..081eff2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -12,10 +12,17 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
         tools:ignore="ScopedStorage" />
-    <!--摄像头相关权限-->
     <uses-feature android:name="android.hardware.camera.any" />
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
     <application
         android:name=".Application"
diff --git a/app/src/main/java/com/wmdigit/cateringdetect/Application.java b/app/src/main/java/com/wmdigit/cateringdetect/Application.java
index 6439a01..678f919 100644
--- a/app/src/main/java/com/wmdigit/cateringdetect/Application.java
+++ b/app/src/main/java/com/wmdigit/cateringdetect/Application.java
@@ -5,6 +5,7 @@ import com.wmdigit.NetworkModule;
 import com.wmdigit.common.CommonModule;
 import com.wmdigit.core.CoreModule;
 import com.wmdigit.data.LocalDataModule;
+import com.wmdigit.service.ServiceModule;
 
 /**
  * @author dizi
@@ -45,5 +46,7 @@ public class Application extends android.app.Application {
         CoreModule.init(this);
         // 初始化网络模块
         NetworkModule.init(this);
+        // 初始化服务模块
+        ServiceModule.init(this);
     }
 }
diff --git a/common/src/main/java/com/wmdigit/common/constants/ErrorCode.java b/common/src/main/java/com/wmdigit/common/constants/ErrorCode.java
new file mode 100644
index 0000000..8a92d54
--- /dev/null
+++ b/common/src/main/java/com/wmdigit/common/constants/ErrorCode.java
@@ -0,0 +1,26 @@
+package com.wmdigit.common.constants;
+
+/**
+ * 错误码
+ * @author dizi
+ */
+public class ErrorCode {
+    // 成功的错误码
+    public static final int ERROR_CODE_SUCCESS = 0;
+
+    // 摄像头不可用的错误码
+    public static final int ERROR_CODE_CAMERA_NOT_AVAILABLE = 2000;
+
+    // 未激活的错误码
+    public static final int ERROR_CODE_NOT_ACTIVATED = 2001;
+
+    // 学习数据初始化未完成的错误码
+    public static final int ERROR_CODE_LEARNING_DATA_INIT_NOT_COMPLETED = 2002;
+
+    // 摄像头未裁剪的错误码
+    public static final int ERROR_CODE_CAMERA_NOT_CROPPED = 2003;
+
+    // 区域内无对象的错误码
+    public static final int ERROR_CODE_NO_OBJECT_IN_AREA = 2004;
+
+}
diff --git a/common/src/main/java/com/wmdigit/common/model/ProductsDTO.java b/common/src/main/java/com/wmdigit/common/model/ProductsDTO.java
index 401e601..4654300 100644
--- a/common/src/main/java/com/wmdigit/common/model/ProductsDTO.java
+++ b/common/src/main/java/com/wmdigit/common/model/ProductsDTO.java
@@ -1,12 +1,15 @@
 package com.wmdigit.common.model;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+
 import androidx.annotation.NonNull;
 
 /**
  * 商品DTO
  * @author dizi
  */
-public class ProductsDTO {
+public class ProductsDTO implements Parcelable {
 
     /**
      * 品名
@@ -40,6 +43,22 @@ public class ProductsDTO {
         this.onSale = onSale;
     }
 
+    protected ProductsDTO(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public static final Creator<ProductsDTO> CREATOR = new Creator<ProductsDTO>() {
+        @Override
+        public ProductsDTO createFromParcel(Parcel in) {
+            return new ProductsDTO(in);
+        }
+
+        @Override
+        public ProductsDTO[] newArray(int size) {
+            return new ProductsDTO[size];
+        }
+    };
+
     @NonNull
     @Override
     public String toString() {
@@ -85,4 +104,35 @@ public class ProductsDTO {
     public void setUnitPrice(String unitPrice) {
         this.unitPrice = unitPrice;
     }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(productName);
+        dest.writeString(productCode);
+        dest.writeString(productMnemonicCode);
+        dest.writeString(unitPrice);
+        if (onSale == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeInt(onSale);
+        }
+    }
+
+    protected void readFromParcel(Parcel in){
+        productName = in.readString();
+        productCode = in.readString();
+        productMnemonicCode = in.readString();
+        unitPrice = in.readString();
+        if (in.readByte() == 0) {
+            onSale = null;
+        } else {
+            onSale = in.readInt();
+        }
+    }
 }
diff --git a/core/src/main/cpp/videopipe_rk3568.cpp b/core/src/main/cpp/videopipe_rk3568.cpp
index 6df6803..5ac233e 100644
--- a/core/src/main/cpp/videopipe_rk3568.cpp
+++ b/core/src/main/cpp/videopipe_rk3568.cpp
@@ -11,7 +11,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
         return JNI_ERR;
     }
-    java_class_video_pipe_manager = (jclass)env->NewGlobalRef(env->FindClass("com/wmdigit/core/videopipe/VideoPipeManager"));
+    java_class_video_pipe_manager = (jclass)env->NewGlobalRef(env->FindClass("com/wmdigit/core/videopipe/VideoPipeRepository"));
 
     return JNI_VERSION_1_6;
 }
@@ -21,9 +21,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
  */
 extern "C"
 JNIEXPORT jint JNICALL
-Java_com_wmdigit_core_videopipe_VideoPipeManager_init(JNIEnv *env, jobject thiz,
-                                                      jstring modelBackBonePath,
-                                                      jstring modelC3dPath) {
+Java_com_wmdigit_core_videopipe_VideoPipeRepository_init(JNIEnv *env, jobject thiz,
+                                                         jstring modelBackBonePath,
+                                                         jstring modelC3dPath) {
     int size_back_bone, size_c3d;
     unsigned char* data_back_bone = read_file_byte(env, modelBackBonePath, size_back_bone);
     unsigned char* data_c3d = read_file_byte(env, modelC3dPath, size_c3d);
@@ -50,8 +50,8 @@ Java_com_wmdigit_core_videopipe_VideoPipeManager_init(JNIEnv *env, jobject thiz,
  */
 extern "C"
 JNIEXPORT jint JNICALL
-Java_com_wmdigit_core_videopipe_VideoPipeManager_setMarkMat(JNIEnv *env, jobject thiz,
-                                                            jobject bitmap) {
+Java_com_wmdigit_core_videopipe_VideoPipeRepository_setMarkMat(JNIEnv *env, jobject thiz,
+                                                               jobject bitmap) {
     // Bitmap转mat
     cv::Mat mat = convert_bitmap_to_mat(env, bitmap);
     // RGBA转RGB
@@ -63,8 +63,8 @@ Java_com_wmdigit_core_videopipe_VideoPipeManager_setMarkMat(JNIEnv *env, jobject
 
 extern "C"
 JNIEXPORT void JNICALL
-Java_com_wmdigit_core_videopipe_VideoPipeManager_feedFrame(JNIEnv *env, jobject thiz,
-                                                           jobject bitmap) {
+Java_com_wmdigit_core_videopipe_VideoPipeRepository_feedFrame(JNIEnv *env, jobject thiz,
+                                                              jobject bitmap) {
     // Bitmap转mat
     cv::Mat mat = convert_bitmap_to_mat(env, bitmap);
     // rgba转rgb
@@ -114,7 +114,7 @@ void feed_frame_callback(int state){
     jmethodID j_method_instance = env->GetStaticMethodID(
             java_class_video_pipe_manager,
             "getInstance",
-            "()Lcom/wmdigit/core/videopipe/VideoPipeManager;"
+            "()Lcom/wmdigit/core/videopipe/VideoPipeRepository;"
     );
     jobject instance=env->CallStaticObjectMethod(java_class_video_pipe_manager, j_method_instance);
     jmethodID j_method_in_out;
diff --git a/core/src/main/java/com/wmdigit/core/CoreModule.java b/core/src/main/java/com/wmdigit/core/CoreModule.java
index 065409e..c82fd71 100644
--- a/core/src/main/java/com/wmdigit/core/CoreModule.java
+++ b/core/src/main/java/com/wmdigit/core/CoreModule.java
@@ -3,10 +3,9 @@ package com.wmdigit.core;
 import android.content.Context;
 
 import com.wmdigit.core.catering.TargetDetectionRepository;
-import com.wmdigit.core.hnsw.Hnsw;
 import com.wmdigit.core.hnsw.HnswRepository;
 import com.wmdigit.core.opencv.OpencvRepository;
-import com.wmdigit.core.videopipe.VideoPipeManager;
+import com.wmdigit.core.videopipe.VideoPipeRepository;
 
 /**
  * Core模块初始化类
@@ -35,7 +34,7 @@ public class CoreModule {
         // 初始化目标检测
         TargetDetectionRepository.getInstance().initTargetDetection();
         // 初始化视频流
-        VideoPipeManager.getInstance().initVideoPipe();
+        VideoPipeRepository.getInstance().initVideoPipe();
         // 初始化索引库
         HnswRepository.getInstance().init();
     }
diff --git a/core/src/main/java/com/wmdigit/core/hnsw/HnswRepository.java b/core/src/main/java/com/wmdigit/core/hnsw/HnswRepository.java
index d5d3102..883450d 100644
--- a/core/src/main/java/com/wmdigit/core/hnsw/HnswRepository.java
+++ b/core/src/main/java/com/wmdigit/core/hnsw/HnswRepository.java
@@ -42,6 +42,7 @@ public class HnswRepository {
      * 记录初始化完成情况
      */
     private boolean initComplete = false;
+    private OnHnswInitListener listener;
 
     public HnswRepository() {
         hnsw = new Hnsw();
@@ -63,6 +64,9 @@ public class HnswRepository {
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribe(o -> {
                     initComplete = true;
+                    if (listener != null){
+                        listener.onInitSuccess();
+                    }
                 });
         compositeDisposable.add(disposable);
     }
@@ -157,7 +161,23 @@ public class HnswRepository {
         hnsw.initHnsw();
     }
 
+    public void setListener(OnHnswInitListener listener) {
+        this.listener = listener;
+        if (initComplete && listener != null){
+            listener.onInitSuccess();
+        }
+    }
+
+    public boolean isInitComplete() {
+        return initComplete;
+    }
+
     public void close(){
         compositeDisposable.clear();
+        listener = null;
+    }
+
+    public static interface OnHnswInitListener{
+        void onInitSuccess();
     }
 }
diff --git a/core/src/main/java/com/wmdigit/core/videopipe/VideoPipeManager.java b/core/src/main/java/com/wmdigit/core/videopipe/VideoPipeRepository.java
similarity index 93%
rename from core/src/main/java/com/wmdigit/core/videopipe/VideoPipeManager.java
rename to core/src/main/java/com/wmdigit/core/videopipe/VideoPipeRepository.java
index fafc31b..4babce7 100644
--- a/core/src/main/java/com/wmdigit/core/videopipe/VideoPipeManager.java
+++ b/core/src/main/java/com/wmdigit/core/videopipe/VideoPipeRepository.java
@@ -11,13 +11,11 @@ import com.wmdigit.data.mmkv.repository.CropLocalRepository;
 import java.io.File;
 import java.io.IOException;
 
-import io.reactivex.disposables.Disposable;
-
 /**
  * 视频流管理类
  * @author dizi
  */
-public class VideoPipeManager {
+public class VideoPipeRepository {
     static {
         System.loadLibrary("video_pipe");
     }
@@ -42,13 +40,13 @@ public class VideoPipeManager {
      */
     private native void feedFrame(Bitmap bitmap);
 
-    private static VideoPipeManager instance;
+    private static VideoPipeRepository instance;
 
-    public static VideoPipeManager getInstance() {
+    public static VideoPipeRepository getInstance() {
         if (instance == null){
-            synchronized (VideoPipeManager.class){
+            synchronized (VideoPipeRepository.class){
                 if (instance == null){
-                    instance = new VideoPipeManager();
+                    instance = new VideoPipeRepository();
                 }
             }
         }
diff --git a/data-local/src/main/java/com/wmdigit/data/database/dao/ProductsDao.java b/data-local/src/main/java/com/wmdigit/data/database/dao/ProductsDao.java
index 94ca5c2..5d1d8e7 100644
--- a/data-local/src/main/java/com/wmdigit/data/database/dao/ProductsDao.java
+++ b/data-local/src/main/java/com/wmdigit/data/database/dao/ProductsDao.java
@@ -30,6 +30,13 @@ public interface ProductsDao {
     @Query("SELECT * FROM Products")
     List<ProductsPO> getAll();
 
+    /**
+     * 查询商品总数
+     * @return
+     */
+    @Query("SELECT COUNT(*) FROM Products")
+    int getCount();
+
     /**
      * 根据关键词获取总数
      * @param keywords
@@ -55,4 +62,5 @@ public interface ProductsDao {
      */
     @Query("SELECT * FROM Products WHERE productCode = :productCode")
     ProductsPO getByProductCode(String productCode);
+
 }
diff --git a/data-local/src/main/java/com/wmdigit/data/database/repository/ProductsRepository.java b/data-local/src/main/java/com/wmdigit/data/database/repository/ProductsRepository.java
index 6686489..64c212c 100644
--- a/data-local/src/main/java/com/wmdigit/data/database/repository/ProductsRepository.java
+++ b/data-local/src/main/java/com/wmdigit/data/database/repository/ProductsRepository.java
@@ -59,6 +59,14 @@ public class ProductsRepository {
         return getProductsDao().getCountByKeywords(keywords);
     }
 
+    /**
+     * 查询商品总数
+     * @return
+     */
+    public int queryCount(){
+        return getProductsDao().getCount();
+    }
+
     /**
      * 根据关键词查询
      * @param keywords
diff --git a/module-demo/src/main/java/com/wmdigit/cateringdetect/demo/ui/fragment/DemoHomeFragment.java b/module-demo/src/main/java/com/wmdigit/cateringdetect/demo/ui/fragment/DemoHomeFragment.java
index fc3b8ba..4042ef8 100644
--- a/module-demo/src/main/java/com/wmdigit/cateringdetect/demo/ui/fragment/DemoHomeFragment.java
+++ b/module-demo/src/main/java/com/wmdigit/cateringdetect/demo/ui/fragment/DemoHomeFragment.java
@@ -7,14 +7,12 @@ import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.LinearLayoutManager;
 
 import com.wmdigit.camera.CameraxController;
-import com.wmdigit.camera.listener.OnImageAnalyzeListener;
 import com.wmdigit.cateringdetect.demo.R;
 import com.wmdigit.cateringdetect.demo.databinding.FragmentDemoHomeBinding;
 import com.wmdigit.cateringdetect.demo.ui.adapter.DemoImagesAdapter;
 import com.wmdigit.cateringdetect.demo.ui.adapter.ShoppingCartAdapter;
 import com.wmdigit.cateringdetect.demo.ui.viewmodel.DemoHomeViewModel;
 import com.wmdigit.common.base.mvvm.BaseMvvmFragment;
-import com.wmdigit.core.videopipe.VideoPipeManager;
 
 
 /**
diff --git a/module-setting/src/main/java/com/wmdigit/setting/viewmodel/DataLearningViewModel.java b/module-setting/src/main/java/com/wmdigit/setting/viewmodel/DataLearningViewModel.java
index a2b997d..5194334 100644
--- a/module-setting/src/main/java/com/wmdigit/setting/viewmodel/DataLearningViewModel.java
+++ b/module-setting/src/main/java/com/wmdigit/setting/viewmodel/DataLearningViewModel.java
@@ -18,13 +18,12 @@ import com.wmdigit.core.catering.TargetDetectionRepository;
 import com.wmdigit.core.catering.model.TargetDetectResult;
 import com.wmdigit.core.hnsw.HnswRepository;
 import com.wmdigit.core.opencv.OpencvRepository;
-import com.wmdigit.core.videopipe.VideoPipeManager;
+import com.wmdigit.core.videopipe.VideoPipeRepository;
 import com.wmdigit.data.database.entity.ProductsPO;
 import com.wmdigit.data.database.mapper.ProductsMapper;
 import com.wmdigit.data.database.repository.FeaturesRepository;
 import com.wmdigit.data.database.repository.ProductsRepository;
 import com.wmdigit.data.mmkv.repository.CropLocalRepository;
-import com.wmdigit.setting.R;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -78,7 +77,7 @@ public class DataLearningViewModel extends BaseViewModel {
     /**
      * 视频流回调
      */
-    private final VideoPipeManager.OnVideoPipeEventListener onVideoPipeEventListener = new VideoPipeManager.OnVideoPipeEventListener() {
+    private final VideoPipeRepository.OnVideoPipeEventListener onVideoPipeEventListener = new VideoPipeRepository.OnVideoPipeEventListener() {
         @Override
         public void onObjectIn() {
             onObjectInEvent();
@@ -103,7 +102,7 @@ public class DataLearningViewModel extends BaseViewModel {
         keywords.postValue("");
         cameraOpened.postValue(false);
         // 设置视频流回调
-        VideoPipeManager.getInstance().setListener(onVideoPipeEventListener);
+        VideoPipeRepository.getInstance().setListener(onVideoPipeEventListener);
     }
 
     /**
@@ -127,11 +126,11 @@ public class DataLearningViewModel extends BaseViewModel {
         // 处理Bitmap
         if (frameCount > 15){
             // 喂给视频流
-            VideoPipeManager.getInstance().processImage(bitmapCopy, true);
+            VideoPipeRepository.getInstance().processImage(bitmapCopy, true);
         }
         else if (frameCount == 15){
             // 视频流设置空盘背景
-            VideoPipeManager.getInstance().setEmptyImage(bitmapCopy, true);
+            VideoPipeRepository.getInstance().setEmptyImage(bitmapCopy, true);
             frameCount++;
         }
         else{
@@ -427,7 +426,7 @@ public class DataLearningViewModel extends BaseViewModel {
     protected void onCleared() {
         super.onCleared();
         // 注销视频流回调
-        VideoPipeManager.getInstance().setListener(null);
+        VideoPipeRepository.getInstance().setListener(null);
         // 释放计划任务
         if (scheduler != null && !scheduler.isShutdown()){
             scheduler.shutdown();
diff --git a/service-sdk/.gitignore b/service-sdk/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/service-sdk/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/service-sdk/build.gradle b/service-sdk/build.gradle
new file mode 100644
index 0000000..a8a940d
--- /dev/null
+++ b/service-sdk/build.gradle
@@ -0,0 +1,54 @@
+plugins {
+    id 'com.android.library'
+}
+
+android {
+    namespace 'com.wmdigit.service'
+    compileSdk 33
+
+    defaultConfig {
+        minSdk 24
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    buildFeatures{
+        aidl true
+    }
+}
+
+dependencies {
+
+    implementation 'androidx.appcompat:appcompat:1.6.1'
+    implementation 'com.google.android.material:material:1.9.0'
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.2.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
+}
+
+task makeJar(type: Jar) {
+    //指定生成的jar名
+//    baseName ('WmCateringService_v1.0.2_sdk')
+    archiveBaseName="WmCateringService_v1.0.2_sdk"
+    //从哪里打包class文件,根据你的AS版本会所有不同
+    //但是一定要能在此路径下可以找得到自己写的类
+    //如果你封装的jar包用起来有问题,很可能是此处出错
+    from('build/intermediates/javac/debug/classes/')
+    //去掉不需要打包的目录和文件
+    exclude('test/','BuildConfig.class','R.class')
+    //去掉R$开头的文件
+    exclude{ it.name.startsWith('R$') }
+}
+
+makeJar.dependsOn(build)
\ No newline at end of file
diff --git a/service/src/main/java/com/wmdigit/service/aidl/model/CMakeLists.txt b/service-sdk/consumer-rules.pro
similarity index 100%
rename from service/src/main/java/com/wmdigit/service/aidl/model/CMakeLists.txt
rename to service-sdk/consumer-rules.pro
diff --git a/service-sdk/proguard-rules.pro b/service-sdk/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/service-sdk/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/service-sdk/src/androidTest/java/com/wmdigit/service/ExampleInstrumentedTest.java b/service-sdk/src/androidTest/java/com/wmdigit/service/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..cb1b1d2
--- /dev/null
+++ b/service-sdk/src/androidTest/java/com/wmdigit/service/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.wmdigit.service;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("com.wmdigit.service.test", appContext.getPackageName());
+    }
+}
\ No newline at end of file
diff --git a/service-sdk/src/main/AndroidManifest.xml b/service-sdk/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a5918e6
--- /dev/null
+++ b/service-sdk/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>
\ No newline at end of file
diff --git a/service-sdk/src/main/aidl/com/wmdigit/common/model/ProductsDTO.aidl b/service-sdk/src/main/aidl/com/wmdigit/common/model/ProductsDTO.aidl
new file mode 100644
index 0000000..c21dc01
--- /dev/null
+++ b/service-sdk/src/main/aidl/com/wmdigit/common/model/ProductsDTO.aidl
@@ -0,0 +1,3 @@
+package com.wmdigit.common.model;
+
+parcelable ProductsDTO;
\ No newline at end of file
diff --git a/service-sdk/src/main/aidl/com/wmdigit/service/ICateringInterface.aidl b/service-sdk/src/main/aidl/com/wmdigit/service/ICateringInterface.aidl
new file mode 100644
index 0000000..64f4d7a
--- /dev/null
+++ b/service-sdk/src/main/aidl/com/wmdigit/service/ICateringInterface.aidl
@@ -0,0 +1,30 @@
+package com.wmdigit.service;
+
+import com.wmdigit.service.IOnInitListener;
+import com.wmdigit.service.IOnDetectionListener;
+import com.wmdigit.service.aidl.model.DetectResult;
+import com.wmdigit.common.model.ProductsDTO;
+
+
+interface ICateringInterface {
+
+    void init(IOnInitListener listener);
+
+    void importProducts(in List<ProductsDTO> products);
+
+    void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener);
+
+    void unregisterDetectionListener();
+
+    DetectResult autoDetect(boolean generateBitmap);
+
+    boolean checkActivation();
+
+    boolean checkCameraCrop();
+
+    boolean checkLearningDataInited();
+
+    void openSettingPage();
+
+    void resetCameraBackground();
+}
\ No newline at end of file
diff --git a/service-sdk/src/main/aidl/com/wmdigit/service/IOnDetectionListener.aidl b/service-sdk/src/main/aidl/com/wmdigit/service/IOnDetectionListener.aidl
new file mode 100644
index 0000000..80b87e8
--- /dev/null
+++ b/service-sdk/src/main/aidl/com/wmdigit/service/IOnDetectionListener.aidl
@@ -0,0 +1,7 @@
+package com.wmdigit.service;
+
+import com.wmdigit.service.aidl.model.DetectResult;
+
+interface IOnDetectionListener {
+    void onDetected(in DetectResult result);
+}
\ No newline at end of file
diff --git a/service-sdk/src/main/aidl/com/wmdigit/service/IOnInitListener.aidl b/service-sdk/src/main/aidl/com/wmdigit/service/IOnInitListener.aidl
new file mode 100644
index 0000000..c208326
--- /dev/null
+++ b/service-sdk/src/main/aidl/com/wmdigit/service/IOnInitListener.aidl
@@ -0,0 +1,6 @@
+package com.wmdigit.service;
+
+
+interface IOnInitListener {
+    void onComplete();
+}
\ No newline at end of file
diff --git a/service-sdk/src/main/aidl/com/wmdigit/service/aidl/model/DetectResult.aidl b/service-sdk/src/main/aidl/com/wmdigit/service/aidl/model/DetectResult.aidl
new file mode 100644
index 0000000..2a8d44f
--- /dev/null
+++ b/service-sdk/src/main/aidl/com/wmdigit/service/aidl/model/DetectResult.aidl
@@ -0,0 +1,2 @@
+package com.wmdigit.service.aidl.model;
+parcelable DetectResult;
diff --git a/service-sdk/src/main/java/com/wmdigit/common/model/ProductsDTO.java b/service-sdk/src/main/java/com/wmdigit/common/model/ProductsDTO.java
new file mode 100644
index 0000000..4654300
--- /dev/null
+++ b/service-sdk/src/main/java/com/wmdigit/common/model/ProductsDTO.java
@@ -0,0 +1,138 @@
+package com.wmdigit.common.model;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/**
+ * 商品DTO
+ * @author dizi
+ */
+public class ProductsDTO implements Parcelable {
+
+    /**
+     * 品名
+     */
+    private String productName;
+    /**
+     * 商品代码
+     */
+    private String productCode;
+    /**
+     * 商品助记码
+     */
+    private String productMnemonicCode;
+    /**
+     * 商品单价(元/份)
+     */
+    private String unitPrice;
+    /**
+     * 在售状态,1:在售  2:下架
+     */
+    private Integer onSale;
+
+    public ProductsDTO() {
+    }
+
+    public ProductsDTO(String productName, String productCode, String productMnemonicCode, String unitPrice, Integer onSale) {
+        this.productName = productName;
+        this.productCode = productCode;
+        this.productMnemonicCode = productMnemonicCode;
+        this.unitPrice = unitPrice;
+        this.onSale = onSale;
+    }
+
+    protected ProductsDTO(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public static final Creator<ProductsDTO> CREATOR = new Creator<ProductsDTO>() {
+        @Override
+        public ProductsDTO createFromParcel(Parcel in) {
+            return new ProductsDTO(in);
+        }
+
+        @Override
+        public ProductsDTO[] newArray(int size) {
+            return new ProductsDTO[size];
+        }
+    };
+
+    @NonNull
+    @Override
+    public String toString() {
+        return super.toString();
+    }
+
+    public String getProductName() {
+        return productName;
+    }
+
+    public void setProductName(String productName) {
+        this.productName = productName;
+    }
+
+    public String getProductCode() {
+        return productCode;
+    }
+
+    public void setProductCode(String productCode) {
+        this.productCode = productCode;
+    }
+
+    public String getProductMnemonicCode() {
+        return productMnemonicCode;
+    }
+
+    public void setProductMnemonicCode(String productMnemonicCode) {
+        this.productMnemonicCode = productMnemonicCode;
+    }
+
+    public String getUnitPrice() {
+        return unitPrice;
+    }
+
+    public Integer getOnSale() {
+        return onSale;
+    }
+
+    public void setOnSale(Integer onSale) {
+        this.onSale = onSale;
+    }
+
+    public void setUnitPrice(String unitPrice) {
+        this.unitPrice = unitPrice;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(productName);
+        dest.writeString(productCode);
+        dest.writeString(productMnemonicCode);
+        dest.writeString(unitPrice);
+        if (onSale == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeInt(onSale);
+        }
+    }
+
+    protected void readFromParcel(Parcel in){
+        productName = in.readString();
+        productCode = in.readString();
+        productMnemonicCode = in.readString();
+        unitPrice = in.readString();
+        if (in.readByte() == 0) {
+            onSale = null;
+        } else {
+            onSale = in.readInt();
+        }
+    }
+}
diff --git a/service-sdk/src/main/java/com/wmdigit/service/WmSdk.java b/service-sdk/src/main/java/com/wmdigit/service/WmSdk.java
new file mode 100644
index 0000000..c63ce63
--- /dev/null
+++ b/service-sdk/src/main/java/com/wmdigit/service/WmSdk.java
@@ -0,0 +1,297 @@
+package com.wmdigit.service;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.wmdigit.common.model.ProductsDTO;
+import com.wmdigit.service.aidl.model.DetectResult;
+import com.wmdigit.service.listener.OnServiceConnectListener;
+
+import java.util.List;
+
+/**
+ * WmSdk类,实现了WmSdkInterface接口,提供了与服务端进行通信的功能
+ * @author dizi
+ */
+public class WmSdk implements WmSdkInterface{
+
+    /**
+     * 服务包名,用于绑定服务
+     */
+    private static final String SERVICE_PACKAGE = "com.wmdigit.cateringdetect";
+    /**
+     * 服务动作,用于绑定服务
+     */
+    private static final String SERVICE_ACTION = "com.wmdigit.service";
+    /**
+     * WmSdk的单例实例
+     */
+    private static WmSdk instance;
+    /**
+     * 获取WmSdk的单例实例
+     * @return WmSdk的实例
+     */
+    public static WmSdk getInstance(){
+        if (instance == null){
+            synchronized (WmSdk.class){
+                if (instance == null){
+                    instance = new WmSdk();
+                }
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * 应用程序上下文
+     */
+    private Context context;
+    /**
+     * AIDL接口对象
+     */
+    private ICateringInterface aidlInterface;
+    /**
+     * 服务连接监听器
+     */
+    private OnServiceConnectListener onServiceConnectListener;
+
+    private boolean isServiceConnected = false;
+
+    /**
+     *  DeathRecipient用于处理aidl接口对象的死亡消息
+     */
+    private final IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            if (aidlInterface != null) {
+                aidlInterface.asBinder().unlinkToDeath(this, 0);
+            }
+        }
+    };
+
+    /**
+     * ServiceConnection对象,用于处理与服务的连接和断开连接
+     */
+    private ServiceConnection serviceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            isServiceConnected = true;
+            aidlInterface = ICateringInterface.Stub.asInterface(service);
+            // 注册DeathRecipient
+            try {
+                aidlInterface.asBinder().linkToDeath(deathRecipient, 0);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+            if (onServiceConnectListener != null){
+                onServiceConnectListener.onConnected(name, service);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            isServiceConnected = false;
+            if (onServiceConnectListener != null){
+                onServiceConnectListener.onDisconnected(name);
+            }
+        }
+    };
+
+    /**
+     * 绑定服务
+     * @param context 应用程序上下文
+     * @param listener 服务连接监听器,用于处理服务连接状态的变化
+     */
+    @Override
+    public void bindService(Context context, OnServiceConnectListener listener) {
+        this.context = context;
+        this.onServiceConnectListener = listener;
+        Intent intent = new Intent();
+        intent.setPackage(SERVICE_PACKAGE);
+        intent.setAction(SERVICE_ACTION);
+        context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    /**
+     * 解绑服务
+     */
+    @Override
+    public void unbindService() {
+        if (serviceConnection != null){
+            context.unbindService(serviceConnection);
+        }
+    }
+
+    /**
+     * 初始化SDK
+     * @param listener 初始化监听器,用于处理初始化过程中的回调
+     */
+    @Override
+    public void init(IOnInitListener listener) {
+        try {
+            aidlInterface.init(listener);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 导入产品数据
+     * @param products 产品数据列表
+     */
+    @Override
+    public void importProducts(List<ProductsDTO> products) {
+        try {
+            aidlInterface.importProducts(products);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 注册检测监听器
+     * @param generateBitmap 是否生成位图
+     * @param listener 检测监听器,用于处理检测过程中的回调
+     */
+    @Override
+    public void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener) {
+        try {
+            aidlInterface.registerDetectionListener(generateBitmap, listener);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 取消注册检测监听器
+     */
+    @Override
+    public void unregisterDetectionListener() {
+        try {
+            aidlInterface.unregisterDetectionListener();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 自动检测
+     * @param generateBitmap 是否生成位图
+     * @return 检测结果
+     */
+    @Override
+    public DetectResult autoDetect(boolean generateBitmap) {
+        DetectResult detectResult = null;
+        try {
+            detectResult = aidlInterface.autoDetect(generateBitmap);
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+        return detectResult;
+    }
+
+    /**
+     * 检查服务应用是否已安装
+     *
+     * @param context Android上下文,用于访问包管理器以查询应用安装状态
+     * @return 如果服务应用已安装,则返回true;否则返回false
+     */
+    @Override
+    public boolean checkServiceAppInstalled(Context context) {
+        try{
+            // 尝试获取服务应用的包信息
+            context.getPackageManager().getPackageInfo(SERVICE_PACKAGE, 0);
+        }
+        catch (PackageManager.NameNotFoundException e){
+            // 如果未找到包,则服务应用未安装
+            return false;
+        }
+        // 如果成功获取到包信息,则服务应用已安装
+        return true;
+    }
+
+    /**
+     * 检查与服务的连接状态
+     *
+     * @return 如果服务连接已建立,则返回true;否则返回false
+     */
+    @Override
+    public boolean checkServiceConnected() {
+        // 返回当前的服务连接状态
+        return isServiceConnected;
+    }
+    /**
+     * 检查激活状态
+     * @return 激活状态,true表示已激活,false表示未激活
+     */
+    @Override
+    public boolean checkActivation() {
+        boolean result = false;
+        try {
+            result = aidlInterface.checkActivation();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    /**
+     * 检查相机裁剪功能
+     * @return 相机裁剪功能状态,true表示支持,false表示不支持
+     */
+    @Override
+    public boolean checkCameraCrop() {
+        boolean result = false;
+        try{
+            result = aidlInterface.checkCameraCrop();
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    /**
+     * 检查学习数据是否已初始化
+     * @return 学习数据初始化状态,true表示已初始化,false表示未初始化
+     */
+    @Override
+    public boolean checkLearningDataInitCompleted() {
+        boolean result = false;
+        try{
+            result = aidlInterface.checkLearningDataInited();
+        } catch (Exception e){
+          e.printStackTrace();
+        }
+        return result;
+    }
+
+    /**
+     * 打开设置页面
+     */
+    @Override
+    public void openSettingPage() {
+        try {
+            aidlInterface.openSettingPage();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 重置相机背景
+     */
+    @Override
+    public void resetCameraBackground() {
+        try {
+            aidlInterface.resetCameraBackground();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
+
diff --git a/service-sdk/src/main/java/com/wmdigit/service/WmSdkInterface.java b/service-sdk/src/main/java/com/wmdigit/service/WmSdkInterface.java
new file mode 100644
index 0000000..c4c8aac
--- /dev/null
+++ b/service-sdk/src/main/java/com/wmdigit/service/WmSdkInterface.java
@@ -0,0 +1,120 @@
+package com.wmdigit.service;
+
+import android.content.Context;
+
+import com.wmdigit.common.model.ProductsDTO;
+import com.wmdigit.service.aidl.model.DetectResult;
+import com.wmdigit.service.listener.OnServiceConnectListener;
+
+import java.util.List;
+
+/**
+ * WmSdk界面接口,定义了一系列与服务交互的方法
+ */
+public interface WmSdkInterface {
+
+    /**
+     * 绑定服务
+     *
+     * @param context 上下文,用于绑定服务
+     * @param listener 服务连接监听器,处理服务连接状态
+     */
+    void bindService(Context context, OnServiceConnectListener listener);
+
+    /**
+     * 解绑服务
+     *
+     * 调用此方法前应确保服务已绑定
+     */
+    void unbindService();
+
+    /**
+     * 初始化SDK
+     *
+     * @param listener 初始化监听器,处理初始化结果
+     */
+    void init(IOnInitListener listener);
+
+    /**
+     * 导入产品信息
+     *
+     * @param products 产品数据传输对象列表,包含需要导入的产品信息
+     */
+    void importProducts(List<ProductsDTO> products);
+
+    /**
+     * 注册检测监听器
+     *
+     * @param generateBitmap 是否生成位图
+     * @param listener 检测事件监听器,处理检测结果
+     */
+    void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener);
+
+    /**
+     * 取消注册检测监听器
+     *
+     * 调用此方法前应确保检测监听器已注册
+     */
+    void unregisterDetectionListener();
+
+    /**
+     * 自动检测
+     *
+     * @param generateBitmap 是否生成位图
+     * @return 检测结果
+     */
+    DetectResult autoDetect(boolean generateBitmap);
+
+
+    /**
+     * 检查服务应用程序是否已安装
+     *
+     * @param context Android应用程序的上下文,用于访问包管理器等系统服务
+     * @return 如果服务应用程序已安装返回true,否则返回false
+     */
+    boolean checkServiceAppInstalled(Context context);
+
+    /**
+     * 检查服务是否已连接
+     *
+     * @return 如果服务已连接返回true,否则返回false
+     */
+    boolean checkServiceConnected();
+
+    /**
+     * 获取激活状态
+     *
+     * @return 激活状态,true表示已激活,false表示未激活
+     */
+    boolean checkActivation();
+
+    /**
+     * 获取摄像头裁剪状态
+     *
+     * @return 裁剪状态,true表示开启裁剪,false表示关闭裁剪
+     */
+    boolean checkCameraCrop();
+
+    /**
+     * 获取学习数据初始化状态
+     *
+     * @return 初始化状态,true表示已初始化,false表示未初始化
+     */
+    boolean checkLearningDataInitCompleted();
+
+    /**
+     * 打开设置页面
+     *
+     * 此方法用于引导用户至应用的设置页面进行相关配置
+     */
+    void openSettingPage();
+
+    /**
+     * 重置摄像头背景
+     *
+     * 调用此方法可以重置摄像头的背景至默认状态
+     */
+    void resetCameraBackground();
+
+}
+
diff --git a/service-sdk/src/main/java/com/wmdigit/service/aidl/model/DetectResult.java b/service-sdk/src/main/java/com/wmdigit/service/aidl/model/DetectResult.java
new file mode 100644
index 0000000..6b11f93
--- /dev/null
+++ b/service-sdk/src/main/java/com/wmdigit/service/aidl/model/DetectResult.java
@@ -0,0 +1,175 @@
+package com.wmdigit.service.aidl.model;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * DetectResult类,表示检测的结果,实现了Parcelable接口,用于组件间数据传递
+ * @author dizi
+ */
+public class DetectResult implements Parcelable {
+
+    /**
+     * 识别结果的状态码,默认为0
+     */
+    private int code = 0;
+    /**
+     * 图像位图
+     */
+    private Bitmap bitmap;
+    /**
+     * 识别到的产品编码列表
+     */
+    private List<String> productCodes;
+
+    public DetectResult() {
+        this.bitmap = null;
+        this.productCodes = new ArrayList<>();
+    }
+
+    /**
+     * 构造函数,初始化检测结果对象
+     *
+     * @param code         识别结果的状态码
+     * @param bitmap       识别的图像位图
+     * @param productCodes 识别到的产品编码列表
+     */
+    public DetectResult(int code, Bitmap bitmap, List<String> productCodes) {
+        this.code = code;
+        this.bitmap = bitmap;
+        this.productCodes = productCodes != null ? new ArrayList<>(productCodes) : new ArrayList<>();
+    }
+
+    /**
+     * 构造函数,用于从Parcel中读取数据并重建对象
+     *
+     * @param in 用于读取数据的Parcel对象
+     */
+    protected DetectResult(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * 创建DetectResult对象的Creator实例
+     *
+     * @return DetectResult对象的Creator实例
+     */
+    public static final Creator<DetectResult> CREATOR = new Creator<DetectResult>() {
+        @Override
+        public DetectResult createFromParcel(Parcel in) {
+            // 从指定的Parcel中读取数据并创建DetectResult对象
+            return new DetectResult(in);
+        }
+
+        @Override
+        public DetectResult[] newArray(int size) {
+            // 创建并返回DetectResult对象的数组
+            return new DetectResult[size];
+        }
+    };
+
+    /**
+     * 描述内容的类型,返回0表示没有需要特殊处理的内容
+     *
+     * @return int类型,表示内容的类型
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * 将当前对象的数据写入到Parcel中
+     * 此方法用于对象的序列化,将对象的状态转换为可以在进程间传输的形式
+     *
+     * @param dest 用于写入数据的Parcel对象
+     * @param flags 写入操作的标志,目前未使用但保留以兼容性
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(code);
+        // 先写入productCodes列表的大小
+        dest.writeInt(productCodes.size());
+        // 使用StringBuilder将所有产品编码拼接成一个字符串
+        StringBuilder sb = new StringBuilder();
+        for (String productCode : productCodes) {
+            sb.append(productCode).append(",");
+        }
+        dest.writeString(sb.toString());
+        // 检查bitmap是否为空,并根据情况写入标志和bitmap数据
+        if (bitmap == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            dest.writeParcelable(bitmap, flags);
+        }
+    }
+
+    /**
+     * 从Parcel中读取数据,恢复当前对象
+     * 此方法用于对象的反序列化,从Parcel数据中恢复对象的状态
+     *
+     * @param in 包含数据的Parcel对象
+     */
+    public void readFromParcel(Parcel in) {
+        code = in.readInt();
+        // 先读取productCodes列表的大小
+        int size = in.readInt();
+        // 根据读取的大小初始化productCodes列表
+        String productCodesStr = in.readString();
+        productCodes = new ArrayList<>();
+        if (productCodesStr != null && !productCodesStr.isEmpty()) {
+            String[] codes = productCodesStr.split(",");
+            for (String code : codes) {
+                if (!code.isEmpty()) {
+                    productCodes.add(code);
+                }
+            }
+        }
+        // 读取bitmap是否存在标志
+        int hasBitmap = in.readInt();
+        // 根据标志恢复bitmap,如果不存在则置为null
+        if (hasBitmap == 0) {
+            bitmap = null;
+        } else {
+            // 如果存在,则读取并恢复bitmap对象
+            try {
+                bitmap = in.readParcelable(Bitmap.class.getClassLoader());
+            } catch (Exception e) {
+                // 异常处理,确保程序不会崩溃
+                bitmap = null;
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public Bitmap getBitmap() {
+        return bitmap;
+    }
+
+    public void setBitmap(Bitmap bitmap) {
+        this.bitmap = bitmap;
+    }
+
+    public List<String> getProductCodes() {
+        return productCodes;
+    }
+
+    public void setProductCodes(List<String> productCodes) {
+        this.productCodes = productCodes != null ? new ArrayList<>(productCodes) : new ArrayList<>();
+    }
+}
diff --git a/service-sdk/src/main/java/com/wmdigit/service/listener/OnServiceConnectListener.java b/service-sdk/src/main/java/com/wmdigit/service/listener/OnServiceConnectListener.java
new file mode 100644
index 0000000..4de8877
--- /dev/null
+++ b/service-sdk/src/main/java/com/wmdigit/service/listener/OnServiceConnectListener.java
@@ -0,0 +1,26 @@
+package com.wmdigit.service.listener;
+
+import android.content.ComponentName;
+import android.os.IBinder;
+
+/**
+ * 服务连接监听器接口,用于监听服务的连接和断开事件
+ */
+public interface OnServiceConnectListener {
+
+    /**
+     * 当服务连接成功时调用
+     *
+     * @param name 服务的组件名称
+     * @param service 服务的Binder对象,用于与服务通信
+     */
+    void onConnected(ComponentName name, IBinder service);
+
+    /**
+     * 当服务断开连接时调用,例如服务进程结束或主动断开连接
+     *
+     * @param name 服务的组件名称
+     */
+    void onDisconnected(ComponentName name);
+
+}
diff --git a/service-sdk/src/test/java/com/wmdigit/service/ExampleUnitTest.java b/service-sdk/src/test/java/com/wmdigit/service/ExampleUnitTest.java
new file mode 100644
index 0000000..9b845de
--- /dev/null
+++ b/service-sdk/src/test/java/com/wmdigit/service/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.wmdigit.service;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}
\ No newline at end of file
diff --git a/service/build.gradle b/service/build.gradle
index 51c68d5..1f86bd3 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -11,6 +11,12 @@ android {
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         consumerProguardFiles "consumer-rules.pro"
+
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = [ AROUTER_MODULE_NAME : project.getName() ]
+            }
+        }
     }
 
     buildTypes {
@@ -37,4 +43,9 @@ dependencies {
     implementation project(path: ':common')
     implementation project(path: ':core')
     implementation project(path: ':data-local')
+    implementation project(path: ':camera')
+    implementation project(path: ':module-setting')
+
+    implementation 'com.alibaba:arouter-api:1.5.2'
+    annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
 }
\ No newline at end of file
diff --git a/service/src/main/AndroidManifest.xml b/service/src/main/AndroidManifest.xml
index a5918e6..d08bc0f 100644
--- a/service/src/main/AndroidManifest.xml
+++ b/service/src/main/AndroidManifest.xml
@@ -1,4 +1,22 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.CateringDetect">
 
+        <service android:name=".aidl.CateringService"
+            android:enabled="true"
+            android:exported="true"
+            android:foregroundServiceType="camera|location">
+
+            <intent-filter android:priority="1000">
+                <action android:name="com.wmdigit.service" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+
+        </service>
+    </application>
 </manifest>
\ No newline at end of file
diff --git a/service/src/main/aidl/com/wmdigit/common/model/ProductsDTO.aidl b/service/src/main/aidl/com/wmdigit/common/model/ProductsDTO.aidl
new file mode 100644
index 0000000..c21dc01
--- /dev/null
+++ b/service/src/main/aidl/com/wmdigit/common/model/ProductsDTO.aidl
@@ -0,0 +1,3 @@
+package com.wmdigit.common.model;
+
+parcelable ProductsDTO;
\ No newline at end of file
diff --git a/service/src/main/aidl/com/wmdigit/service/ICateringInterface.aidl b/service/src/main/aidl/com/wmdigit/service/ICateringInterface.aidl
index 5f8a9ff..64f4d7a 100644
--- a/service/src/main/aidl/com/wmdigit/service/ICateringInterface.aidl
+++ b/service/src/main/aidl/com/wmdigit/service/ICateringInterface.aidl
@@ -3,17 +3,20 @@ package com.wmdigit.service;
 import com.wmdigit.service.IOnInitListener;
 import com.wmdigit.service.IOnDetectionListener;
 import com.wmdigit.service.aidl.model.DetectResult;
+import com.wmdigit.common.model.ProductsDTO;
 
 
 interface ICateringInterface {
 
     void init(IOnInitListener listener);
 
-    void registerDetectionListener(IOnDetectionListener listener);
+    void importProducts(in List<ProductsDTO> products);
+
+    void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener);
 
     void unregisterDetectionListener();
 
-    DetectResult autoDetect();
+    DetectResult autoDetect(boolean generateBitmap);
 
     boolean checkActivation();
 
diff --git a/service/src/main/aidl/com/wmdigit/service/IOnDetectionListener.aidl b/service/src/main/aidl/com/wmdigit/service/IOnDetectionListener.aidl
index acaca72..80b87e8 100644
--- a/service/src/main/aidl/com/wmdigit/service/IOnDetectionListener.aidl
+++ b/service/src/main/aidl/com/wmdigit/service/IOnDetectionListener.aidl
@@ -3,5 +3,5 @@ package com.wmdigit.service;
 import com.wmdigit.service.aidl.model.DetectResult;
 
 interface IOnDetectionListener {
-    void onDetected(out DetectResult result);
+    void onDetected(in DetectResult result);
 }
\ No newline at end of file
diff --git a/service/src/main/java/com/wmdigit/service/ServiceModule.java b/service/src/main/java/com/wmdigit/service/ServiceModule.java
new file mode 100644
index 0000000..4f0906b
--- /dev/null
+++ b/service/src/main/java/com/wmdigit/service/ServiceModule.java
@@ -0,0 +1,35 @@
+package com.wmdigit.service;
+
+import android.content.Context;
+
+/**
+ * ServiceModule类作为应用程序上下文的存储和提供者,主要用于在应用的各个部分之间共享上下文信息
+ */
+public class ServiceModule {
+
+    /**
+     * 保存应用程序的全局上下文,全局上下文在整个应用中是唯一的
+     */
+    private static Context appContext;
+
+    /**
+     * 初始化ServiceModule,设置应用程序上下文
+     *
+     * @param context 应用程序上下文,用于初始化appContext
+     */
+    public static void init(Context context){
+        if (appContext == null){
+            appContext = context;
+        }
+    }
+
+    /**
+     * 获取应用程序的全局上下文
+     *
+     * @return 返回保存的应用程序全局上下文,如果未初始化则返回null
+     */
+    public static Context getAppContext() {
+        return appContext;
+    }
+}
+
diff --git a/service/src/main/java/com/wmdigit/service/aidl/CateringInterfaceImpl.java b/service/src/main/java/com/wmdigit/service/aidl/CateringInterfaceImpl.java
index a8ed17a..79d39b6 100644
--- a/service/src/main/java/com/wmdigit/service/aidl/CateringInterfaceImpl.java
+++ b/service/src/main/java/com/wmdigit/service/aidl/CateringInterfaceImpl.java
@@ -1,65 +1,417 @@
 package com.wmdigit.service.aidl;
 
+import android.content.Intent;
+import android.graphics.Bitmap;
 import android.os.RemoteException;
 
+import androidx.lifecycle.LifecycleOwner;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.elvishew.xlog.XLog;
+import com.wmdigit.camera.CameraxController;
+import com.wmdigit.camera.listener.OnImageAnalyzeListener;
+import com.wmdigit.common.constants.ErrorCode;
+import com.wmdigit.common.constants.RouteConstant;
+import com.wmdigit.common.model.ProductsDTO;
+import com.wmdigit.common.utils.ParcelHelper;
+import com.wmdigit.core.catering.TargetDetectionRepository;
+import com.wmdigit.core.catering.model.TargetDetectResult;
+import com.wmdigit.core.hnsw.HnswRepository;
+import com.wmdigit.core.opencv.OpencvRepository;
+import com.wmdigit.core.videopipe.VideoPipeRepository;
+import com.wmdigit.data.database.entity.ProductsPO;
+import com.wmdigit.data.database.repository.ProductsRepository;
+import com.wmdigit.data.mmkv.repository.CropLocalRepository;
+import com.wmdigit.data.mmkv.repository.UserLocalRepository;
 import com.wmdigit.service.ICateringInterface;
 import com.wmdigit.service.IOnDetectionListener;
 import com.wmdigit.service.IOnInitListener;
+import com.wmdigit.service.ServiceModule;
 import com.wmdigit.service.aidl.model.DetectResult;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
 /**
  * 餐饮AIDL服务实现类
  * @author dizi
  */
 public class CateringInterfaceImpl extends ICateringInterface.Stub{
+    /**
+     * 声明一个生命周期所有者,用于管理组件的生命周期
+     */
+    private LifecycleOwner lifecycleOwner;
 
     /**
-     * 初始化
-     * @param listener
-     * @throws RemoteException
+     * 声明一个检测监听器接口实例,用于处理检测事件
+     */
+    private IOnDetectionListener onDetectionListener;
+
+    /**
+     * 构造函数
+     * @param lifecycleOwner 生命周期所有者,用于管理CateringInterfaceImpl实例的生命周期
+     */
+    public CateringInterfaceImpl(LifecycleOwner lifecycleOwner) {
+        this.lifecycleOwner = lifecycleOwner;
+    }
+
+    /**
+     * 初始化接口
+     * @param listener 初始化完成后的回调监听器,当初始化完成时会被调用
+     * @throws RemoteException 如果远程过程调用失败,会抛出此异常
      */
     @Override
     public void init(IOnInitListener listener) throws RemoteException {
+        XLog.i("调用接口:init");
+        // 设置初始化完成的监听器
+        if (listener != null){
+            HnswRepository.getInstance().setListener(() -> {
+                try {
+                    listener.onComplete();
+                } catch (RemoteException e) {
+                    XLog.e(e.toString());
+                }
+            });
+        }
+        // 如果相机没有打开,则设置相机的帧间隔并绑定生命周期所有者
+        if (!CameraxController.getInstance(ServiceModule.getAppContext()).isOpen()) {
+            CameraxController.getInstance().setFrameInterval(2).setLifecycleOwner(lifecycleOwner).apply();
+        }
+    }
 
+    /**
+     * 导入产品信息到数据库
+     * 该方法用于接收一组产品信息对象,并将它们批量插入到数据库中
+     * 在执行插入前,会检查传入的产品信息列表是否为null,以避免空指针异常
+     * 如果产品信息列表为null,则方法直接返回,不执行任何操作
+     * 否则,将使用产品仓库(ProductsRepository)的实例来执行批量插入操作
+     *
+     * @param products 待导入的产品信息列表,每个ProductsDTO对象包含一个产品的所有相关信息
+     * @throws RemoteException 如果与远程系统通信时发生错误
+     */
+    @Override
+    public void importProducts(List<ProductsDTO> products) throws RemoteException {
+        XLog.i("调用接口:importProducts");
+        if (products == null){
+            return;
+        }
+        ProductsRepository.getInstance().insertBatch(products);
+        XLog.i("插入商品数据完成,数据库商品数:%s", ProductsRepository.getInstance().queryCount());
     }
 
+    /**
+     * 注册检测监听器
+     * @param generateBitmap 标志位,指示是否生成位图
+     * @param listener 检测事件的监听器,当有检测事件时会被调用
+     * @throws RemoteException 如果远程过程调用失败,会抛出此异常
+     */
     @Override
-    public void registerDetectionListener(IOnDetectionListener listener) throws RemoteException {
+    public void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener) throws RemoteException {
+        XLog.i("调用接口:registerDetectionListener");
+        // 设置图像分析监听器
+        CameraxController.getInstance(ServiceModule.getAppContext()).setOnImageAnalyzeListener(this::analyzeImage);
+        // 设置视频管道事件监听器
+        VideoPipeRepository.getInstance().setListener(new VideoPipeRepository.OnVideoPipeEventListener() {
+            @Override
+            public void onObjectIn() {
+                onObjectInEvent(generateBitmap);
+            }
 
+            @Override
+            public void onObjectOut() {
+                onObjectOutEvent();
+            }
+        });
+        // 保存检测监听器实例
+        this.onDetectionListener = listener;
     }
 
+
+    /**
+     * 取消注册检测监听器
+     * 此方法会取消注册相关的检测监听器,并释放相关资源
+     * @throws RemoteException 如果远程调用失败
+     */
     @Override
     public void unregisterDetectionListener() throws RemoteException {
-
+        XLog.i("调用接口:unregisterDetectionListener");
+        this.onDetectionListener = null;
+        CameraxController.getInstance().setOnImageAnalyzeListener(null);
+        VideoPipeRepository.getInstance().setListener(null);
+        if (scheduler != null && !scheduler.isShutdown()){
+            scheduler.shutdown();
+        }
+        if (currentTask != null && !currentTask.isCancelled()){
+            currentTask.cancel(false);
+        }
     }
 
+    /**
+     * 自动检测方法
+     * 此方法用于自动检测,可以根据需要生成位图
+     * @param generateBitmap 是否生成位图
+     * @return DetectResult 检测结果
+     * @throws RemoteException 如果远程调用失败
+     */
     @Override
-    public DetectResult autoDetect() throws RemoteException {
-        return null;
+    public DetectResult autoDetect(boolean generateBitmap) throws RemoteException {
+        int code = checkDetectValid();
+        if (code != ErrorCode.ERROR_CODE_SUCCESS){
+            return new DetectResult(code, null, new ArrayList<>());
+        }
+        return captureAndProcessImage(bitmapCopy, generateBitmap);
     }
 
+    /**
+     * 检查激活状态
+     * 此方法用于检查当前用户是否已激活
+     * @return boolean 用户是否已激活
+     * @throws RemoteException 如果远程调用失败
+     */
     @Override
     public boolean checkActivation() throws RemoteException {
-        return false;
+        return UserLocalRepository.getInstance().getIsActivation();
     }
 
+    /**
+     * 检查相机裁剪状态
+     * 此方法用于检查相机是否已经过裁剪
+     * @return boolean 相机是否已经过裁剪
+     * @throws RemoteException 如果远程调用失败
+     */
     @Override
     public boolean checkCameraCrop() throws RemoteException {
-        return false;
+        return CropLocalRepository.getInstance().getHasCropped();
     }
 
+    /**
+     * 检查学习数据初始化状态
+     * 此方法用于检查学习数据是否已经初始化完成
+     * @return boolean 学习数据是否已经初始化完成
+     * @throws RemoteException 如果远程调用失败
+     */
     @Override
     public boolean checkLearningDataInited() throws RemoteException {
-        return false;
+        return HnswRepository.getInstance().isInitComplete();
     }
 
+    /**
+     * 打开设置页面
+     * 该方法通过ARouter导航到设置页面
+     * 使用ARouter进行页面跳转可以实现更灵活的页面路由管理
+     */
     @Override
     public void openSettingPage() throws RemoteException {
-
+        XLog.i("调用接口:openSettingPage");
+        ARouter.getInstance().build(RouteConstant.ROUTE_SETTING).navigation();
     }
 
+    /**
+     * 重置摄像头背景
+     * 该方法用于重置摄像头背景的相关参数
+     * 通过重置frameCount的值,可以恢复默认的摄像头背景行为
+     */
     @Override
     public void resetCameraBackground() throws RemoteException {
+        XLog.i("调用接口:resetCameraBackground");
+        frameCount = 10;
+    }
+
+    /**
+     * 帧计数器,用于跟踪已处理的帧数
+     */
+    private int frameCount = 0;
+    /**
+     * bitmapCopy 用于存储 bitmap 的副本,以便在分析过程中不会修改原始图像
+     */
+    private Bitmap bitmapCopy;
+
+    /**
+     * 分析图像方法
+     *
+     * 本方法的目的是分析给定的 Bitmap 对象,并根据当前帧计数(frameCount)执行不同的操作
+     * 当 frameCount 超过 15 时,将 bitmapCopy 发送给 VideoPipeRepository 进行图像处理
+     * 当 frameCount 等于 15 时,将 bitmapCopy 设置为 VideoPipeRepository 的空图像指示,并增加 frameCount
+     * 否则,只增加 frameCount
+     *
+     * @param bitmap 要分析的 Bitmap 对象
+     */
+    private void analyzeImage(Bitmap bitmap) {
+        // 创建 bitmap 的副本,以避免修改原始 bitmap
+        bitmapCopy = ParcelHelper.copy(bitmap);
+        // 根据帧计数执行不同的操作
+        if (frameCount > 15) {
+            // 如果帧计数超过 15,将副本发送到 VideoPipeRepository 进行处理
+            VideoPipeRepository.getInstance().processImage(bitmapCopy, true);
+        } else if (frameCount == 15) {
+            // 如果帧计数等于 15,将副本设置为 VideoPipeRepository 的空图像,并增加帧计数
+            VideoPipeRepository.getInstance().setEmptyImage(bitmapCopy, true);
+            frameCount++;
+        } else {
+            // 否则,只增加帧计数
+            frameCount++;
+        }
+    }
+
+    /**
+     * 定义变量以指示是否有物体进入指定区域
+     */
+    private boolean objectInArea;
+    /**
+     * 创建一个单线程的定时任务调度器
+     */
+    private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
+    /**
+     * 用于存储当前执行的定时任务
+     */
+    private ScheduledFuture<?> currentTask;
+    /**
+     * 定义一个对象作为处理检测结果的锁
+     */
+    private final Object detectResultLock = new Object();
+
+    /**
+     * 处理物体进入事件
+     * 当物体进入指定区域时,此方法将被调用进行处理
+     *
+     * @param generateBitmap 指示是否生成位图的布尔值,用于控制是否生成检测图像
+     */
+    private void onObjectInEvent(boolean generateBitmap){
+        // 设置标志表示有物体进入区域
+        objectInArea = true;
+        int code = checkDetectValid();
+        if (code != ErrorCode.ERROR_CODE_SUCCESS){
+            if (onDetectionListener != null){
+                try {
+                    onDetectionListener.onDetected(new DetectResult(code, null, new ArrayList<>()));
+                } catch (RemoteException e) {
+                    XLog.e(e.toString());
+                }
+            }
+            return;
+        }
+        // 如果当前有未完成的定时任务,取消该任务
+        if (currentTask != null && !currentTask.isDone()){
+            currentTask.cancel(false);
+        }
+        // 检查线程池是否关闭
+        if (scheduler == null || scheduler.isShutdown()){
+            scheduler = Executors.newSingleThreadScheduledExecutor();
+        }
+        // 安排一个新的定时任务,在2秒后执行物体检测
+        currentTask = scheduler.schedule(() -> {
+            // 执行图像捕获和处理,获取检测结果
+            DetectResult detectResult = captureAndProcessImage(null, generateBitmap);
+            // 如果存在检测结果监听器,调用其onDetected方法
+            if (onDetectionListener != null){
+                try {
+                    onDetectionListener.onDetected(detectResult);
+                } catch (RemoteException e) {
+                    XLog.e(e.toString());
+                }
+            }
+        }, 2, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 处理物体离开事件
+     * 当物体离开指定区域时,此方法将被调用进行处理
+     */
+    private void onObjectOutEvent(){
+        // 设置标志表示物体已离开区域
+        objectInArea = false;
+        // 如果当前任务未被取消,取消当前任务
+        if (currentTask != null && !currentTask.isCancelled()){
+            currentTask.cancel(false);
+        }
+    }
+
+    /**
+     * 捕获并处理图像以进行对象检测
+     *
+     * 此方法接受一个Bitmap对象和一个布尔值,指示是否生成处理后的Bitmap
+     * 它首先检查检测区域中是否有对象,如果没有,立即返回错误代码
+     * 如果有对象,它将根据传入的Bitmap和一系列检测逻辑来处理图像
+     * 最后,根据输入参数决定是否生成并返回处理后的Bitmap
+     *
+     * @param bitmap 用于检测的Bitmap对象,可以为null,如果为null,将使用备用Bitmap
+     * @param generateBitmap 布尔值,指示是否生成并返回处理后的Bitmap
+     * @return DetectResult 包含检测结果和可能的处理后Bitmap的对象
+     */
+    private DetectResult captureAndProcessImage(Bitmap bitmap, boolean generateBitmap){
+        synchronized (detectResultLock) {
+            DetectResult detectResult = new DetectResult();
+            // 如果检测区域中没有对象,设置错误代码并返回
+            if (!objectInArea) {
+                detectResult.setCode(ErrorCode.ERROR_CODE_NO_OBJECT_IN_AREA);
+                return detectResult;
+            }
+            TargetDetectResult targetDetectResult;
+            // 如果传入的Bitmap为空,使用备用的Bitmap
+            if (bitmap == null) {
+                bitmap = ParcelHelper.copy(bitmapCopy);
+            }
+            // 对图像进行处理,尝试检测目标
+            targetDetectResult = TargetDetectionRepository.getInstance().processImage(bitmap, true);
+            // 如果没有检测到任何矩形对象,直接返回
+            if (targetDetectResult.getRectArray() == null) {
+                return detectResult;
+            }
+            List<ProductsPO> productsPOList = new ArrayList<>();
+            // 根据检测到的特征检索产品,并将结果添加到列表中
+            for (int i = 0; i < targetDetectResult.getFeatures().length; i++) {
+                ProductsPO product = HnswRepository.getInstance().retrieveByFeature(targetDetectResult.getFeatures()[i]);
+                productsPOList.add(product);
+            }
+            targetDetectResult.setProducts(productsPOList);
+            // 遍历检测到的产品,将产品代码添加到结果中
+            for (ProductsPO temp : targetDetectResult.getProducts()){
+                detectResult.getProductCodes().add(temp.getProductCode());
+            }
+            // 如果需要生成Bitmap,进行相关处理
+            if (generateBitmap) {
+                // 在Bitmap上绘制检测结果
+                detectResult.setBitmap(OpencvRepository.getInstance().drawDetectResultOnBitmap(targetDetectResult));
+            }
+            return detectResult;
+        }
+    }
+
+    /**
+     * 检查摄像头检测是否有效的功能方法
+     * 该方法用于在进行摄像头检测任务之前,检查多个先决条件是否满足
+     *
+     * @return 返回错误代码,不同的错误代码表示不同的错误类型,如果所有条件都满足,则返回成功代码
+     */
+    private int checkDetectValid(){
+        // 检查摄像头是否已打开
+        if (!CameraxController.getInstance().isOpen()){
+            // 如果摄像头未打开,返回摄像头不可用错误代码
+            return ErrorCode.ERROR_CODE_CAMERA_NOT_AVAILABLE;
+        }
+        // 检查是否已完成激活
+        else if (!UserLocalRepository.getInstance().getIsActivation()){
+            // 如果未激活,返回未激活错误代码
+            return ErrorCode.ERROR_CODE_NOT_ACTIVATED;
+        }
+        // 检查学习数据是否已初始化完成
+        else if (!HnswRepository.getInstance().isInitComplete()){
+            // 如果学习数据未初始化完成,返回学习数据初始化未完成错误代码
+            return ErrorCode.ERROR_CODE_LEARNING_DATA_INIT_NOT_COMPLETED;
+        }
+        // 检查是否已进行裁剪操作
+        else if (!CropLocalRepository.getInstance().getHasCropped()){
+            // 如果未进行裁剪操作,返回摄像头未裁剪错误代码
+            return ErrorCode.ERROR_CODE_CAMERA_NOT_CROPPED;
+        }
+        // 所有条件都满足时,返回成功代码
+        else {
+            return ErrorCode.ERROR_CODE_SUCCESS;
+        }
 
     }
 }
diff --git a/service/src/main/java/com/wmdigit/service/aidl/CateringService.java b/service/src/main/java/com/wmdigit/service/aidl/CateringService.java
index dd4e55d..0626e5c 100644
--- a/service/src/main/java/com/wmdigit/service/aidl/CateringService.java
+++ b/service/src/main/java/com/wmdigit/service/aidl/CateringService.java
@@ -1,6 +1,14 @@
 package com.wmdigit.service.aidl;
 
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ServiceInfo;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -10,6 +18,7 @@ import androidx.lifecycle.LifecycleService;
 
 import com.wmdigit.service.ICateringInterface;
 import com.wmdigit.service.IOnInitListener;
+import com.wmdigit.service.R;
 
 /**
  * 餐饮AIDL服务
@@ -20,7 +29,7 @@ public class CateringService extends LifecycleService {
     /**
      * AIDL接口实现
      */
-    private final ICateringInterface.Stub stub = new CateringInterfaceImpl();
+    private final ICateringInterface.Stub stub = new CateringInterfaceImpl(this);
 
     @Nullable
     @Override
@@ -28,4 +37,61 @@ public class CateringService extends LifecycleService {
         super.onBind(intent);
         return stub;
     }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        createNotificationChannel();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopForeground(true);
+        android.os.Process.killProcess(android.os.Process.myPid());
+    }
+
+    private static final String CHANNEL_ID = "my_channel_02";
+    private static final String CHANNEL_NAME = "服务通知";
+    private static final String CHANNEL_DESCRIPTION = "识别服务运行中";
+    private static final int IMPORTANCE = NotificationManager.IMPORTANCE_HIGH;
+    private static final int NOTIFICATION_ID = 1;
+    private static final int FOREGROUND_SERVICE_TYPES = FOREGROUND_SERVICE_TYPE_LOCATION | ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+
+    private void createNotificationChannel() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+            Notification notification = new Notification.Builder(this)
+                    .setContentTitle("通知")
+                    .setContentText("识别服务运行中")
+//                    .setSmallIcon(R.drawable.ic_launcher_foreground)  // 启用小图标设置
+                    .build();
+            startForeground(NOTIFICATION_ID, notification);
+            return;
+        }
+
+        NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        if (mNotificationManager == null) {
+            throw new IllegalStateException("Failed to get NotificationManager");
+        }
+
+        NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, IMPORTANCE);
+        mChannel.setDescription(CHANNEL_DESCRIPTION);
+        mChannel.enableVibration(false);
+        // mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});  // 如果需要振动则启用此行
+        mNotificationManager.createNotificationChannel(mChannel);
+
+        Notification notification = new Notification.Builder(this)
+                .setContentTitle("通知")
+                .setContentText("识别服务正在运行")
+//                .setSmallIcon(R.drawable.ic_launcher_foreground)  // 启用小图标设置
+                .setChannelId(CHANNEL_ID)
+                .build();
+
+        if (Build.VERSION.SDK_INT >= 30) {
+            startForeground(NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPES);
+        } else {
+            startForeground(NOTIFICATION_ID, notification);
+        }
+    }
+
 }
diff --git a/service/src/main/java/com/wmdigit/service/aidl/model/DetectResult.java b/service/src/main/java/com/wmdigit/service/aidl/model/DetectResult.java
index 003a1d0..6b11f93 100644
--- a/service/src/main/java/com/wmdigit/service/aidl/model/DetectResult.java
+++ b/service/src/main/java/com/wmdigit/service/aidl/model/DetectResult.java
@@ -9,13 +9,16 @@ import androidx.annotation.NonNull;
 import java.util.ArrayList;
 import java.util.List;
 
-
 /**
  * DetectResult类,表示检测的结果,实现了Parcelable接口,用于组件间数据传递
  * @author dizi
  */
 public class DetectResult implements Parcelable {
 
+    /**
+     * 识别结果的状态码,默认为0
+     */
+    private int code = 0;
     /**
      * 图像位图
      */
@@ -33,12 +36,14 @@ public class DetectResult implements Parcelable {
     /**
      * 构造函数,初始化检测结果对象
      *
-     * @param bitmap      识别的图像位图
+     * @param code         识别结果的状态码
+     * @param bitmap       识别的图像位图
      * @param productCodes 识别到的产品编码列表
      */
-    public DetectResult(Bitmap bitmap, List<String> productCodes) {
+    public DetectResult(int code, Bitmap bitmap, List<String> productCodes) {
+        this.code = code;
         this.bitmap = bitmap;
-        this.productCodes = productCodes;
+        this.productCodes = productCodes != null ? new ArrayList<>(productCodes) : new ArrayList<>();
     }
 
     /**
@@ -88,17 +93,19 @@ public class DetectResult implements Parcelable {
      */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(code);
         // 先写入productCodes列表的大小
         dest.writeInt(productCodes.size());
-        // 遍历productCodes列表,逐个写入每个元素
+        // 使用StringBuilder将所有产品编码拼接成一个字符串
+        StringBuilder sb = new StringBuilder();
         for (String productCode : productCodes) {
-            dest.writeString(productCode);
+            sb.append(productCode).append(",");
         }
+        dest.writeString(sb.toString());
         // 检查bitmap是否为空,并根据情况写入标志和bitmap数据
-        if (bitmap == null){
+        if (bitmap == null) {
             dest.writeInt(0);
-        }
-        else{
+        } else {
             dest.writeInt(1);
             dest.writeParcelable(bitmap, flags);
         }
@@ -110,25 +117,59 @@ public class DetectResult implements Parcelable {
      *
      * @param in 包含数据的Parcel对象
      */
-    public void readFromParcel(Parcel in){
+    public void readFromParcel(Parcel in) {
+        code = in.readInt();
         // 先读取productCodes列表的大小
         int size = in.readInt();
         // 根据读取的大小初始化productCodes列表
-        productCodes = new ArrayList<>(size);
-        // 遍历读取的大小,逐个读取并添加到productCodes列表
-        for (int i = 0; i < size; i++){
-            productCodes.add(in.readString());
+        String productCodesStr = in.readString();
+        productCodes = new ArrayList<>();
+        if (productCodesStr != null && !productCodesStr.isEmpty()) {
+            String[] codes = productCodesStr.split(",");
+            for (String code : codes) {
+                if (!code.isEmpty()) {
+                    productCodes.add(code);
+                }
+            }
         }
         // 读取bitmap是否存在标志
         int hasBitmap = in.readInt();
         // 根据标志恢复bitmap,如果不存在则置为null
-        if (hasBitmap == 0){
+        if (hasBitmap == 0) {
             bitmap = null;
-        }
-        else{
+        } else {
             // 如果存在,则读取并恢复bitmap对象
-            bitmap = in.readParcelable(Bitmap.class.getClassLoader());
+            try {
+                bitmap = in.readParcelable(Bitmap.class.getClassLoader());
+            } catch (Exception e) {
+                // 异常处理,确保程序不会崩溃
+                bitmap = null;
+                e.printStackTrace();
+            }
         }
     }
 
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public Bitmap getBitmap() {
+        return bitmap;
+    }
+
+    public void setBitmap(Bitmap bitmap) {
+        this.bitmap = bitmap;
+    }
+
+    public List<String> getProductCodes() {
+        return productCodes;
+    }
+
+    public void setProductCodes(List<String> productCodes) {
+        this.productCodes = productCodes != null ? new ArrayList<>(productCodes) : new ArrayList<>();
+    }
 }
diff --git a/settings.gradle b/settings.gradle
index b0ae41b..47c7862 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -34,3 +34,4 @@ include ':module-setting'
 include ':data-remote'
 include ':service'
 include ':opencv'
+include ':service-sdk'
-- 
2.18.1