Android system service analysis and Native Service example

Posted by koolaid on Tue, 04 Jan 2022 20:58:09 +0100

preface

In the previous article: Implementation of Android remote Service based on AIDL programming This paper introduces how to realize remote service through AIDL in Android, However, the customized remote service is not registered as a System Service. Many functions provided by Android to users are realized through system services. This paper will learn to record the relevant knowledge of Android system services, and learn another form of remote service - Native Service, in addition to the Java layer service written by ALDL.

System Service

Use the command adb shell service list to list the system service list information of the current Android system:

Let's first understand how the System Java Service is defined and generated. Refer to the article: Introduction to Android system service.

Vibrator service analysis

Let's look at how a system service is established from a simple system service, Vibrator service (vibrator: [android.os.IVibratorService]). The Vibrator service provides an interface to control mobile phone vibration. Applications can call the Vibrator interface to make the mobile phone vibrate to remind users.

It can be seen from the official Android documentation that Vibrator is only an abstract class with only four abstract interfaces:

abstract void cancel() Cancel vibration
abstract boolean hasVibrator() Whether there is vibration function
abstract void vibrate(long[] pattern, int repeat) Repeat the vibration in rhythm
abstract void vibrate(long milliseconds) Continuous vibration

1. The method of using vibration service in the application is also very simple, such as making the mobile phone vibrate for 500 milliseconds:

Vibrator mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mVibrator.vibrate(500);

2. You can see from the document that vibrator is only defined in Android An abstract class in the OS package, whose location in the source code is frameworks / base / core / Java / Android / OS / vibrator Java, which instance is actually used in the application? The vibrator instance used in the application is obtained through a method getSystemService(Context.VIBRATOR_SERVICE) of Context, and the implementation of Context is generally in ContextImpl. Let's take a look at how ContextImpl implements getSystemService:

// frameworks/base/core/java/android/app/ContextImpl.java
@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

3. SystemServiceRegistry is only available after Android 6.0. The code before Android 6.0 does not have this class. The following code is written directly in ContextImpl:

//frameworks/base/core/java/android/app/SystemServiceRegistry.java
 public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

4,SYSTEM_SERVICE_MAP is a HashMap. Take a ServiceFetcher from the HashMap through the name string of our service, and then return the ServiceFetcher's getService(). What is a ServiceFetcher? What is its getService()? Since he's from SYSTEM_SERVICE_MAP is obtained from the HashMap, so find out what the HashMap put. The following code can be found by searching SystemServiceRegistry:

private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

5. Here to system_ SERVICE_ A pair of key/value pairs composed of String and ServiceFetcher are put in the map. Where is registerService() called? If you continue to search, you can find many codes similar to the following:

static {
    registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
            new CachedServiceFetcher<AccessibilityManager>() {
        @Override
        public AccessibilityManager createService(ContextImpl ctx) {
            return AccessibilityManager.getInstance(ctx);
        }});

    ...
    registerService(Context.VIBRATOR_SERVICE, Vibrator.class,
            new CachedServiceFetcher<Vibrator>() {
        @Override
        public Vibrator createService(ContextImpl ctx) {
            return new SystemVibrator(ctx);
        }});
    ...
}

6. In the static code block of system service registry, many system services are registered through registerService, including the vibrator we are investigating_ Service. By combining the above analysis code, we can know that getSystemService(Context.VIBRATOR_SERVICE) obtains an instance of SystemVibrator. By viewing the code of SystemVibrator, we can also find that SystemVibrator does inherit from vibrator:

public class SystemVibrator extends Vibrator {
    ...
}

7. Let's look at how the vibration control of the system is realized from the SystemVibrator. Taking hasVibrator() as an example, this is to query whether the current system can vibrate. In SystemVibrator, its implementation is as follows:

public boolean hasVibrator() {
    ...
    try {
        return mService.hasVibrator();
    } catch (RemoteException e) {
    }
    ...
}

8. Here, an mService is called directly Hasvibrator(), what is mService? Where did you come from? Search and find:

private final IVibratorService mService;
public SystemVibrator() {
    ...
    mService = IVibratorService.Stub.asInterface(
            ServiceManager.getService("vibrator"));
}

9. mService is an IVibratorService. Let's leave IVibratorService alone Stub. What's the matter with asinterface? Let's take a look at what IVibratorService is. Search the code and find that this is not a java file, but an aidl file:

frameworks/base/core/java/android/os/IVibratorService.aidl

AIDL (Android Interface Definition Language) is an interface definition file in Android, which provides a simple cross process communication method for the system. Several interfaces are defined in IVibratorService and are also used in SystemVibrator, including hasVibrator():

interface IVibratorService
{
    boolean hasVibrator();
    void vibrate(...);
    void vibratePattern(...);
    void cancelVibrate(IBinder token);
}

10. Here is only the interface definition. Where is the interface implementation? Through grep search in the frameworks/base directory or AndroidXRef search, you can find that the implementation of IVibratorService interface is in frameworks/base / services / Java / COM / Android / server / vibratorservice java:

public class VibratorService extends IVibratorService.Stub

11. You can see that VibratorService implements all the interfaces defined by IVibratorService and calls to the native layer through JNI for lower level implementation. The lower level implementation is not the content discussed in this document. What we need to analyze is how VibratorService becomes a system service. So how does VibratorService register as a system service? In SystemServer:

VibratorService vibrator = null;
...
//Instantiate VibratorService and add it to ServiceManager
traceBeginAndSlog("StartVibratorService");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
...
//Notify the service system that startup is complete
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeVibratorServiceReady");
try {
    vibrator.systemReady();
} catch (Throwable e) {
    reportWtf("making Vibrator Service ready", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

In this way, in SystemVibrator, you can connect to the VibratorService through the following code to communicate with the underlying system services:

IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));

mService is equivalent to an agent of IVibratorService in the application layer. All implementations are still in the VibratorService of system server. When looking at the code, you can find that registerService is called statically in the static code block, so each Manager obtained by getSystemServcr is also a singleton.

System service implementation process

Draw a diagram, which can be summarized as follows:

From the above analysis, we can summarize the whole implementation process of Vibrator service:

1. Define an abstract class Vibrator, which defines some abstract methods that can be accessed in the application:
frameworks/base/core/java/android/os/Vibrator.java;

2. Define a specific class SystemVibrator, inherit the Vibrator, and implement the abstract method:
frameworks/base/core/java/android/os/SystemVibrator.java;

3. Define an AIDL interface file IVibratorService to define the system service interface:
frameworks/base/core/java/android/os/IVibratorService.aidl

4. Define the service VibratorService and implement the interface defined by IVibratorService:
frameworks/base/services/java/com/android/server/VibratorService.java

public class VibratorService extends IVibratorService.Stub

5. Add VibratorService to system service:
frameworks/base/services/java/com/android/server/SystemServer.java

VibratorService vibrator = null;
...
//Instantiate VibratorService and add it to ServiceManager
Slog.i(TAG, "Vibrator Service");
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
...
//Notify the service system that startup is complete
try {
    vibrator.systemReady();
} catch (Throwable e) {
    reportWtf("making Vibrator Service ready", e);
}

6. Connect to the VibratorService through the agent of IVibratorService in SystemVibrator, so that the interface of IVibratorService can be called in the interface implementation of SystemVibrator:
frameworks/base/core/java/android/os/SystemVibrator.java

private final IVibratorService mService;
...
public SystemVibrator() {
    ...
    mService = IVibratorService.Stub.asInterface(
            ServiceManager.getService("vibrator"));
    ...
    public boolean hasVibrator() {
        ...
        try {
            return mService.hasVibrator();
        } catch (RemoteException e) {
        }
        ...
    }
}

7. Define a string representing the Vibrator service in the Context:
frameworks/base/core/java/android/content/Context.java

public static final String VIBRATOR_SERVICE = "vibrator";

8. Add the instantiation process of SystemVibrator in ContextImpl:
frameworks/base/core/java/android/app/ContextImpl.java

registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
    return new SystemVibrator(ctx);
}});  

9. Use the Vibrator interface in the application:

Vibrator mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mVibrator.vibrate(500);

10. To ensure normal compilation, you also need to add the AIDL file to the compilation configuration framework / base / Android mk:

LOCAL_SRC_FILES += \
...
core/java/android/os/IVibratorService.aidl \

Native Service

Android system services are roughly divided into three categories: local daemon, Native system services and Java system services, as shown in the figure below:

Local daemon

The init process is based on init RC file, start the local daemon. These processes will reside in the system. Some will be started only once, and some will be started by init if they exit. The specific startup method is in init RC. Here are a few daemons and their functions.

Native system services

Native Service, a feature of the Android system, is a Remote Service written in C + + or C code for Java to call remotely, Because C/C + + code generates native code (machine code), so it is called Native Service. Java language can't make system calls directly. You must call C code through JNI to access system functions, but Native Service is completely different. C + + has the ability to make system calls directly. Therefore, when accessing operating system or hardware functions, JNI is no longer needed, and you can call directly. The code implementation will be improved More unified. Obviously, the execution efficiency of native code is much higher than that of Java, an interpretative language. With the increasing performance requirements of Android system, the demand for Native Service will be higher and higher.

Native services run in local daemons. For example, mediaserver daemons include AudioFlinger, MediaPlayerService, CameraService, AudioPolicyService, SoundTriggerHwService and other services. In the main function of mediaserver process, initialize the instances of these services. The code is as follows:

int main(int argc __unused, char** argv)
{
    ...
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    ALOGI("ServiceManager: %p", sm.get());
    AudioFlinger::instantiate();
    MediaPlayerService::instantiate();
    CameraService::instantiate();
    AudioPolicyService::instantiate();
    SoundTriggerHwService::instantiate();
    ...
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

The Native system service will be registered in the ServiceManager when the process is initialized. In this way, other applications or services can call Native system services through the binder mechanism. Of course, we can also develop a Native system service and implement its binder interface, so that other applications or services in the Native layer can call the service. If the Native system service we developed wants to be provided to Java layer applications, we need to implement a java interface, and then call the Native system service through JNI.


And stub in System Service of Java type Corresponding to the implementation of Proxy object, Native Service will also define BpXXX object, where B represents Binder, p code Proxy, and the required interface name is XXX. Because the Binder communication to be sent is sent through the BpBinder::transact() method, the Proxy of Java environment and Native environment is essentially the same thing, but provides different implementations in different programming language environments. Meanwhile, at the receiving and processing end, when the IPCThreadState object calls back to the ontransact () referenced by the BBinder, the BBinder at this time no longer refers to a JavaBBinder object, but an extended BnXXX object, and n represents Native. So BBinder::transact() will directly call onTransact() implemented in BnXXX. In this onTransact() method, you can process the Binder message and return the result.

Bluetooth settings example

The Demo project structure is as follows:

IDeviceMac.h) interface documents

Taking an example of reading and setting Bluetooth address as an example, the interface name is IDeviceMac, and the code is as follows:

#ifndef XTC_IDEVICEMAC_H
#define XTC_IDEVICEMAC_H

#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <utils/String8.h>
#include <android/log.h>

#ifdef TAG
#undef TAG
#endif
#define TAG "DeviceMac"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)

namespace android {

class IDeviceMac : public IInterface {
public:
    enum {
        SET_BT_MAC = IBinder::FIRST_CALL_TRANSACTION,
        GET_BT_MAC,
    };

    virtual int setBTMac(String8 bt) = 0;

    virtual String8 getBTMac() = 0;

    DECLARE_META_INTERFACE(DeviceMac);
};

class BnDeviceMac : public BnInterface<IDeviceMac> {
public:
    virtual status_t onTransact( uint32_t code,
                                const Parcel& data,
                                Parcel* reply,
                                uint32_t flags);
};
   
} // end namespace android
#endif

The above code is simple:

  1. Define an IDeviceMac class that inherits from the interface class IInterface (IInterface, like the Java environment, can be used to provide asBinder() method and return an IBinder reference). The IDeviceMac class defines the service interfaces provided externally (such as setBTMac(), getBTMac());
  2. Also note DECLARE_META_INTERFACE(DeviceMac); Is a macro definition used to define the two methods that must be implemented to inherit IInterface, asInterface() function and getInterfaceDescriptor() function;
  3. IBinder in Java environment will have asinterface () interface method, but IBinder implemented in libbinder through C + + cannot provide this interface, so it needs to pass a globally effective interface_cast() macro to complete this function, interface_cast() is to call an undefined INTERFACE::asInterface() macro, so the interface will be called only where asInterface() is clearly defined_ Cast() is valid;
  4. We can see that after defining IDeviceMac, we also define a class BnDeviceMac, which is a specification of Binder call, that is, after defining Ixxx interface, Bpxxx represents Client-side interface, Bnxxx represents Service-side interface, both Bpxxx and Bnxxx need us to implement specific contents, and the methods in Bnxxx and Bpxxx correspond to those in Ixxx one by one.

IDeviceMac.cpp interface implementation file

Next, continue to write idevicemac Cpp file:

#include "IDeviceMac.h"

namespace android {

class BpDeviceMac : public BpInterface<IDeviceMac> {

public:
    BpDeviceMac(const sp<IBinder>& impl) : BpInterface<IDeviceMac>(impl)
    {
    }

    int setBTMac(String8 bt) {
        LOGI("Bp setBT");
        Parcel data, reply;
        data.writeInterfaceToken(IDeviceMac::getInterfaceDescriptor());
        data.writeString8(bt);
        remote()->transact(SET_BT_MAC, data, &reply);
        return reply.readInt32();
    }

    String8 getBTMac() {
        LOGI("Bp getBT");
        Parcel data, reply;
        data.writeInterfaceToken(IDeviceMac::getInterfaceDescriptor());
        remote()->transact(GET_BT_MAC, data, &reply);
        return reply.readString8();
    }
};

IMPLEMENT_META_INTERFACE(DeviceMac, "DeviceMac");
/* Macro above expands to code below.
const android::String16 IDeviceMac::descriptor("DeviceMac");
const android::String16& IDeviceMac::getInterfaceDescriptor() const {
    return IDeviceMac::descriptor;
}
android::sp<IDeviceMac> IDeviceMac::asInterface(const android::sp<android::IBinder>& obj) {
    android::sp<IDeviceMac> intr;
    if (obj != NULL) {
        intr = static_cast<IDeviceMac*>(obj->queryLocalInterface(IDeviceMac::descriptor).get());
        if (intr == NULL) {
            intr = new BpDeviceMac(obj);
        }
    }
    return intr;
}
*/

status_t BnDeviceMac::onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    CHECK_INTERFACE(IDeviceMac, data, reply);
    LOGI("Bn onTransact code:%d", code);
    switch(code) {
        case SET_BT_MAC:
            reply->writeInt32(setBTMac(data.readString8()));
            return NO_ERROR;
        case GET_BT_MAC:
            reply->writeString8(getBTMac());
            return NO_ERROR;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

} // end namespace android

Simple interpretation of code:

  1. Implement in the above code_ META_ INTERFACE(DeviceMac, "DeviceMac"); The content commented out below is the actual code represented by this macro definition, that is, idevicemac The macro definition in H actually defines these two methods;
  2. The content of BpDeviceMac is to write the relevant parameters to Parcel, which is a class for reading and writing the parameters across the process. Then calling remote () ->transact (), calling to BnDeviceMac::onTransact(), BnDeviceMac::onTransact() function has already crossed the process, and how to do it specifically involves the principle of IPC, which is not discussed here.
  3. The next thing that defines BnDeviceMac::onTransact is also very simple. It reads the data that is passed from Client from Parcel, and then calls the corresponding implementation method in BnDeviceMac. Here we need to pay attention. Because BnDeviceMac::onTransact() code and BpDeviceMac are written in the same file, it looks a little like BnDeviceMac calling BpDeviceMac. In fact, the setBTMac() getBTMac() called in BnDeviceMac::onTranscat() is the method implemented in calling BnDeviceMac, and then we will talk about the implementation of BnDeviceMac.

DeviceMacService service implementation file

1,DeviceMacService.h as follows:

#ifndef XTC_DEVICEMACSERVICE_H
#define XTC_DEVICEMACSERVICE_H

#include "IDeviceMac.h"

#define SERVER_NAME "DeviceMacService"

namespace android {

class DeviceMacService : public BnDeviceMac {
public:
    DeviceMacService();
    virtual ~DeviceMacService();
    //IDeviceMac
    virtual int setBTMac(String8 bt);
    virtual String8 getBTMac();
};

} // end namespace android
#endif

2,DeviceMacService.cpp is as follows:

#include "DeviceMacService.h"

namespace android {

DeviceMacService::DeviceMacService() {

}

DeviceMacService::~DeviceMacService() {

}

int DeviceMacService::setBTMac(String8 bt) {
    LOGI("Bn setBT, bt:%s", bt.string());
    return NO_ERROR;
}

String8 DeviceMacService::getBTMac() {
    LOGI("Bn getBT");
    return String8("4a:4b:4c:3a:3b:3c");
}

} // end namespace android

DeviceMacService inherits BnDeviceMac and implements its methods, so the relevant calls in BnDeviceMac::onTransact() method will be called to DeviceMacService. In DeviceMacService, we can do what we actually want to do.

Register Service and Client side calls

main_server.cpp files are as follows:

#include "DeviceMacService.h"
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>

using namespace android;

sp<IDeviceMac> getService() {
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->getService(String16(SERVER_NAME));
    //interfa_ Function of cast()
    sp<IDeviceMac> service = interface_cast<IDeviceMac>(binder);
    return service;
}

int main(int argc, char** argv) {
    if (argc == 1) {
        LOGI("start DeviceMacService");
        //The addService() function is used to register the service
        defaultServiceManager()->addService(String16(SERVER_NAME), new DeviceMacService());
        android::ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    } else if (argc == 2) {
        sp<IDeviceMac> devMacServer = getService();
        devMacServer->setBTMac(String8("1a:1b:1c:1a:1b:1c"));
        String8 bt = devMacServer->getBTMac();
        LOGI("get bt mac:%s", bt.string());
    }
    return 0;
}

Pay attention to the following points in the above code:

  1. The code for adding a service is very simple, with three lines of code and fixed operation;
  2. There is an interfa in the process of obtaining the service_ The cast function will take IBinder as a parameter to new a BpDeviceMac object. We will call the relevant interface through this object and finally call DeviceMacService;
  3. Note: for the convenience of testing, add Service and call Service are written in the same executable file, and the actual projects are separated.

Code compilation and running call

Now everything is ready, just wait for the compiler to run, Android MK code is as follows:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := macserver
LOCAL_MODULE_TAGS := optional

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
        frameworks/native/include \
        system/core/include

LOCAL_SRC_FILES := IDeviceMac.cpp DeviceMacService.cpp main_server.cpp
LOCAL_SHARED_LIBRARIES := libutils libcutils libbinder libhardware

include $(BUILD_EXECUTABLE)

Compilation and operation methods:

  1. Ensure that the current Android source code is fully compiled (some dependencies need to be compiled first);
  2. Put the service directory into the Android source directory (such as vendor/qcom/service);
  3. Execute mmm vendor/qcom/service in the root directory of Android source code;
  4. The executable file compiled after execution is under out/target/product/xxx/system/bin / (XXX is the product of lunch);
  5. Push the compiled executable file macserver to the mobile phone system/bin / through adb (adb needs root, that is, execute adb root and adb remount);
  6. Execute adb shell chmod 777 /system/bin/macserver plus executable permissions, then start the service and execute ADB shell / system / bin / macserver (it will block the current window);
  7. Re open a window and execute the adb command adb shell /system/bin/macserver 1 to call the Service. You can filter DeviceMac through logcat to view the log.

If you want to start the Service automatically after startup and specify the user group to which the Service belongs, you can click init Add the following code to RC:

service macserver /system/bin/macserver 
     class main
     user root
     group root

Development - write AIDL

If you want to write less code and call it more conveniently, there is also an implementation method, that is, write AIDL files, which is similar to AIDL in Java, but you need to compile them in the Android source code, and the system will automatically compile them according to Ixxx AIDL generates Ixxx during compilation CPP, the cpp file is the same as the idevicemac. Com file we wrote above The contents of CPP are basically the same, that is, this part of the code can be generated automatically. Then you only need to write a class on the Service side to inherit Bnxxx and then implement the methods defined in the AIDL file. It is very convenient to use. The icameraservice.com on Android 7.1 AIDL is implemented in this way. Some codes are as follows. You can refer to:
frameworks/av/camera/aidl/android/hardware/ICameraService.aidl

  /**
     * Types for getNumberOfCameras
     */
    const int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0;
    const int CAMERA_TYPE_ALL = 1;

    /**
     * Return the number of camera devices available in the system
     */
    int getNumberOfCameras(int type);

    /**
     * Fetch basic camera information for a camera device
     */
    CameraInfo getCameraInfo(int cameraId);

    /**
     * Default UID/PID values for non-privileged callers of
     * connect(), connectDevice(), and connectLegacy()
     */
    const int USE_CALLING_UID = -1;
    const int USE_CALLING_PID = -1;

    /**
     * Open a camera device through the old camera API
     */
    ICamera connect(ICameraClient client,
            int cameraId,
            String opPackageName,
            int clientUid, int clientPid);

If implemented in this way, the compiled Android The following codes need to be added to MK:

LOCAL_AIDL_INCLUDES := \
    frameworks/av/camera/aidl \

LOCAL_SRC_FILES := \
    aidl/android/hardware/ICameraService.aidl \

That is, the header file path and aidl source file should be imported.

If you want to see the automatically generated Ixxx CPP code. The path is: out/target/product/xxx1/obj/xxx2/xxx3_intermediates/aidl-generated/
xxx1 represents the product you selected when you lunch, and xxx2 represents the module type you compiled, usually SHARED_LIBRARIES or
EXECUTABLES, xxx3 means local in the module you compiled_ The name of the module definition. For example: out/target/product/msm8953/obj/SHARED_LIBRARIES/libcamera_client_intermediates/aidl-generated/src/aidl/android/hardware/ICameraService.cpp.

Examples of addition, subtraction, multiplication and division

The directory structure is as follows:

  1. Create a folder called arithmetic to create Android mk,ArithmeticService.cpp,ArithmeticService.h and iarithmeticservice CPP these four files;
  2. Create another include subfolder in the arithmetic folder and create an iarithmeticservice H) documents;
  3. Add another ARI in the arithmetic folder_ Client directory, add Android MK and main_client.cpp file.

Let's start writing code.

IArithmeticService.h

First create an iarithmeticservice Class h, which exists as the parent class of BpArithmeticService and BnArithmeticService. Here we define the Binder working function to be completed, and define the BnArithmeticService class. The code is as follows:

#ifndef ANDROID_IARITHMETIC_H
#define ANDROID_IARITHMETIC_H

#include <utils/Errors.h>  // for status_t
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>

namespace android {
    
    class IArithmeticService : public IInterface
    {
        public:
            // Important macro definitions that provide the asInterface method and descriptor member of the Service
            DECLARE_META_INTERFACE(ArithmeticService);
            
            // Actual working member function
            virtual double add(double a, double b) = 0;
            virtual double div(double a, double b) = 0;
            virtual double mul(double a, double b) = 0;
            virtual double sub(double a, double b) = 0;
    };
    
    class BnArithmeticService : public BnInterface<IArithmeticService>
    {
    public:
        virtual status_t onTransact( uint32_t code, const Parcel & data, Parcel * reply, uint32_t flags = 0);       
    };    
}

#endif

IArithmeticService.cpp

In this file, you need to complete the actual writing of BpArithmeticService and BnArithmeticService classes.

#include <stdint.h>
#include <sys/types.h>

#include <binder/Parcel.h>
#include <binder/IMemory.h>
#include <IArithmeticService.h>

#include <utils/Errors.h>
#include <utils/String8.h>

namespace android {
    // Defines the code value transmitted by Binder
    // Note that the first value must be IBinder::FIRST_CALL_TRANSACTION
    enum {
        ADD = IBinder::FIRST_CALL_TRANSACTION,
        SUB,
        MUL,
        DIV 
    };
    
    // BpArithmeticService inherits from the BpInterface template class
    class BpArithmeticService : public BpInterface<IArithmeticService>
    {
    public:
        BpArithmeticService(const sp<IBinder>& impl)
                : BpInterface<IArithmeticService>(impl)
        {           
        }
        
        // As mentioned earlier, no special work is done, just package the data and send it
        virtual double add(double a, double b)
        {
            Parcel data, reply;
            double result;
            data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor());
            data.writeDouble(a);
            data.writeDouble(b);
            remote()->transact(ADD, data, &reply);
            reply.readDouble(&result);
            return result;
        }
        
        virtual double sub(double a, double b)
        {
            Parcel data, reply;
            double result;
            data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor());
            data.writeDouble(a);
            data.writeDouble(b);
            remote()->transact(SUB, data, &reply);
            reply.readDouble(&result);
            return result;
        }
        
        virtual double mul(double a, double b)
        {
            Parcel data, reply;
            double result;
            data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor());         
            data.writeDouble(a);
            data.writeDouble(b);
            remote()->transact(MUL, data, &reply);
            reply.readDouble(&result);
            return result;
        }
        
        virtual double div(double a, double b)
        {
            Parcel data, reply;
            double result;
            data.writeInterfaceToken(IArithmeticService::getInterfaceDescriptor());
            data.writeDouble(a);
            data.writeDouble(b);
            remote()->transact(DIV, data, &reply);
            reply.readDouble(&result);
            return result;
        }
    };
    
    // Key macros, complete declare_ META_ Methods defined in the interface macro
    IMPLEMENT_META_INTERFACE(ArithmeticService, "ArithmeticService");
    
    // The BnArithmeticService::onTransact method is defined according to the specific code value as described above
    // Call the actual method for data processing, and write the result to the reply to return
    status_t BnArithmeticService::onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        switch(code) {
            case ADD: {
                CHECK_INTERFACE(IArithmeticService, data, reply);
                const double a = data.readDouble();
                const double b = data.readDouble();
                double result = add(a, b);
                reply->writeDouble(result);
                return NO_ERROR;
            } break;
            
            case SUB: {
                CHECK_INTERFACE(IArithmeticService, data, reply);
                const double a = data.readDouble();
                const double b = data.readDouble();
                double result = sub(a, b);
                reply->writeDouble(result);
                return NO_ERROR;
            } break;
            
            case MUL: {
                CHECK_INTERFACE(IArithmeticService, data, reply);
                const double a = data.readDouble();
                const double b = data.readDouble();
                double result = mul(a, b);
                reply->writeDouble(result);
                return NO_ERROR;
            } break;
            
            case DIV: {
                CHECK_INTERFACE(IArithmeticService, data, reply);
                const double a = data.readDouble();
                const double b = data.readDouble();
                double result = div(a, b);
                reply->writeDouble(result);
                return NO_ERROR;
            } break;
            
            default:
                return BBinder::onTransact(code, data, reply, flags);
            
        }
    }
}

ArithmeticService.h

Here is the actual Service class, which is inherited from the BnXXXXXService class.

#include <utils/Errors.h>
#include "include/IArithmeticService.h"

namespace android {
    
    class ArithmeticService : public BnArithmeticService
    {
    public:
        ArithmeticService();
        // Called when registering a service
        static void instantiate();
        
        virtual double add(double a, double b);
        virtual double sub(double a, double b);
        virtual double mul(double a, double b);
        virtual double div(double a, double b);
    };    
}

ArithmeticService.cpp

Final Service file:

#define LOG_TAG "ArithmeticService"
#include <utils/Log.h>
#include <cutils/log.h>

#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/String16.h> 

#include <IArithmeticService.h>

#include "ArithmeticService.h"

namespace android{

// For registering service
void ArithmeticService::instantiate() {
    ALOGD("%s start", __FUNCTION__);
    defaultServiceManager()->addService(String16("arithmetic"), new ArithmeticService());
}

ArithmeticService::ArithmeticService() {
    ALOGD("ArithmeticService constructor.");    
}

double ArithmeticService::add(double a, double b) {
    double result = a + b;
    ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result);
    
    return result;
}

double ArithmeticService::sub(double a, double b) {
    double result = a - b;
    ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result);
    
    return result;
}

double ArithmeticService::mul(double a, double b) {
    double result = a * b;
    ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result);
    
    return result;
}

double ArithmeticService::div(double a, double b) {
    double result = a / b;
    ALOGD("a = %lf, b = %lf, result = %lf", a ,b, result);
    
    return result;
}
}

Start setup and compile run

1. Add startup code

First, in framework / AV / media / MediaServer / Android Local in MK_ C_ Add the following line in includes: framework/arithmetic / \, and remember to add a '\' backslash at the end; In local_ SHARED_ Add the following line to libareas: libarithmeticservice \ of course, remember the last '\' backslash.

Then we imitate the MediaPlayerService in the main_ mediaserver. Add the startup code of ArithmeticService to the cpp file. Of course, you can also write a c program to start the service

...............
#include "ArithmeticService.h"
..................
    MediaPlayerService::instantiate();
    ArithmeticService::instantiate();
    ResourceManagerService::instantiate();

2. Selinux permission settings

Selinux permission setting is divided into three steps

(1)First, when services are needed, services need to have a definition type,So we're service.te The document is for us service Order one type: 
type arithmetic_service,            service_manager_type;
(2)by service Defines a type After that, you need to put this type Given to us service Yes, we are service_contexts Add the following code to service After getting up, it's type namely arithmetic_service The following:
arithmetic      u:object_r:arithmetic_service:s0
(3)Finally, add allow Rules, because it's our business service Yes MediaServer Loaded in, and so we're mediaserver.te Add the following to the file allow Rules:
allow mediaserver arithmetic_service:service_manager {add find};

3. Compile run

Then write the corresponding Android MK file:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
        ArithmeticService.cpp \
        IArithmeticService.cpp
        
LOCAL_SHARED_LIBRARIES := \
        libbinder           \
        libcutils           \
        liblog          \
        libutils            \
        
LOCAL_C_INCLUDES :=             \
        $(TOP)/frameworks/arithmetic/include    \
        $(TOP)/frameworks/native/include        
        
LOCAL_CLANG := true

LOCAL_MODULE := libarithmeticservice

LOCAL_32_BIT_ONLY := true

include $(BUILD_SHARED_LIBRARY)

include $(call all-makefiles-under,$(LOCAL_PATH))

Finished writing Android MK file, directly put the entire arithmetic folder under the framework folder under the root directory of the Android source code and compile it. After compilation, you can find a libarithmeticservice named libarithmeticservice in the out/target/product/{Project}/system/lib directory So file.

Then, the machine is started. The simple way to judge whether the service is up is to connect the phone with adb after the phone is turned on, and then list the currently running services of the phone through the service list command. If everything is OK, the arithmetic service we added will run.

main_client.cpp

Write client program:

#define LOG_TAG "ArithmeticClient"
#include <utils/Log.h>
#include <cutils/log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/String16.h> 
#include <utils/RefBase.h>

#include <IArithmeticService.h>

#include "ArithmeticService.h"

using namespace android;

int main(int artc __unused, char ** argv __unused) 
{
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->getService(String16("arithmetic"));
    sp<IArithmeticService> ArithmeticService;
    ArithmeticService = interface_cast<IArithmeticService>(binder);
    
    double result_add = ArithmeticService->add(1.0, 2.0);
    ALOGD("Call Add method: 1.0 + 2.0 = %lf", result_add);
    
    double result_sub = ArithmeticService->sub(1201.2, 32.10);
    ALOGD("Call Sub method: 1201.2 + 32.10 = %lf", result_sub);
        
    double result_mul = ArithmeticService->mul(32.5, 40.2);
    ALOGD("Call Mul method: 32.5 + 40.2 = %lf", result_mul);
    
    double result_div = ArithmeticService->div(1000.0, 4);
    ALOGD("Call Div method: 1000.0 + 4 = %lf", result_div);    
}

Android. Contents of MK file:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
        main_client.cpp
        
LOCAL_SHARED_LIBRARIES := \
        libbinder                           \
        libcutils                           \
        liblog                          \
        libutils                            \
        libarithmeticservice  \
        
LOCAL_C_INCLUDES :=             \
    $(TOP)/frameworks/arithmetic \
    $(TOP)/frameworks/arithmetic/include  \
        $(TOP)/frameworks/native/include        
        
LOCAL_CLANG := true

LOCAL_MODULE := arithmeticclient

LOCAL_32_BIT_ONLY := true

include $(BUILD_EXECUTABLE)

include $(call all-makefiles-under,$(LOCAL_PATH))

According to the original directory hierarchy, put the updated code under the framework folder and compile it with mmm. You can see an arithmetics client executable program under the out/target/product/{Project}/system/bin path. If the user version is compiled before, you can only execute make to brush the machine. If the eng version of the software can plug in the mobile phone through usb, execute adb remount, and then push this file to the mobile phone / system/bin path, and then execute it directly. Because the Log output of Android is used, you need to use ADB shell logcat arithmetics client: D arithmetics service: D *: S - V threadtime to see the content of the final output.

summary

Reference article: