Android从驱动到应用开发实例

一、增加驱动

1. 文件目录:

    modified:   lichee/linux-3.10/.config
    modified:   lichee/linux-3.10/drivers/Kconfig
    modified:   lichee/linux-3.10/drivers/Makefile
    new file:   lichee/linux-3.10/drivers/demo/Kconfig
    new file:   lichee/linux-3.10/drivers/demo/Makefile
    new file:   lichee/linux-3.10/drivers/demo/demo.c
    new file:   lichee/linux-3.10/drivers/demo/demo.h

2. 代码

lichee/linux-3.10/.config

增加:

CONFIG_DEMO=y

lichee/linux-3.10/drivers/Kconfig

增加:

source "drivers/demo/Kconfig"

lichee/linux-3.10/drivers/Makefile

增加:

obj-$(CONFIG_DEMO)     += demo/

新增文件:lichee/linux-3.10/drivers/demo/Kconfig

config DEMO
       tristate "Demo Register Driver"
       default y
       help
       This is the deomo driver for android system.

新增文件:lichee/linux-3.10/drivers/demo/Makefile

obj-$(CONFIG_DEMO) += demo.o

新增文件:lichee/linux-3.10/drivers/demo/demo.h

#ifndef _DEMO_H_
#define _DEMO_H_
 
#include <linux/cdev.h>
#include <linux/semaphore.h>
 
#define DEMO_DEVICE_NODE_NAME "demo"
#define DEMO_DEVICE_FILE_NAME "demo"
#define DEMO_DEVICE_PROC_NAME "demo"
#define DEMO_DEVICE_CLASS_NAME "demo"
 
struct demo_reg_dev
{
        int val;
        struct semaphore sem;   // 信号量
        struct cdev dev;
};
 
#endif

增加文件:lichee/linux-3.10/drivers/demo/demo.c

#include <linux/init.h>
#include <linux/module.h>
//#include <linux/type.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
 
#include "demo.h"
 
static int demo_major = 0;
static int demo_minor = 0;
 
static struct class* demo_class = NULL;
static struct demo_reg_dev* demo_dev = NULL;
 
static int demo_open(struct inode* inode, struct file* filp);
static int demo_release(struct inode* inode, struct file* filp);
static ssize_t demo_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);
static ssize_t demo_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);
 
static struct file_operations demo_fops =
{
	.owner = THIS_MODULE,
	.open = demo_open,
	.release = demo_release,
	.read = demo_read,
	.write = demo_write,
};
 
#define SEQ_printf(m, x...)	\
	do {	\
		if (m)	\
			seq_printf(m, x);	\
		else	\
			pr_err(x);	\
	} while(0);
 
 
 
static int demo_proc_show(struct seq_file* m, void* v)
{
	SEQ_printf(m, "%d\n", demo_dev->val);
	return 0;
}
 
static int demo_proc_open(struct inode* inode, struct file* filp)
{
	return single_open(filp, demo_proc_show, inode->i_private);
}
 
static ssize_t __demo_set_val(struct demo_reg_dev* dev, const char* buf, size_t count)
{
	int val = 0;
 
	val = simple_strtol(buf, NULL, 10);
 
	if (down_interruptible(&(dev->sem)))
	{
		return -ERESTARTSYS;
	}
 
	dev->val = val;
	up(&(dev->sem));
 
	return count;
}
 
static ssize_t demo_proc_write(struct file* filp, const char* ubuf, size_t cnt, loff_t* data)
{
	int err = 0;
	char* page = NULL;
	
	if (cnt > PAGE_SIZE)
	{
		printk(KERN_ALERT"The buff is too large: %lu.\n", cnt);
        	return -EFAULT;
	}
	
	page = (char*)__get_free_page(GFP_KERNEL);
	if (!page)
	{
		printk(KERN_ALERT"Failed to alloc page.\n");
        	return -ENOMEM;
	}
	
	if (copy_from_user(page, ubuf, cnt))
	{
		printk(KERN_ALERT"Failed to copy buff from user.\n");
        	err = -EFAULT;
        	goto out;
	}
 
	err = __demo_set_val(demo_dev, page, cnt);
 
out:
	free_page((unsigned long)page);
	return err;
}
 
static const struct file_operations demo_proc_fops = 
{
	.open = demo_proc_open,
	.write = demo_proc_write,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
};
 
static ssize_t demo_val_show(struct device* dev, struct device_attribute* attr, char* buf);
static ssize_t demo_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);
 
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, demo_val_show, demo_val_store);
 
// static struct device_attribute dev_attr_val = {
// 	.attr = {
// 		.name = "demo_val",
// 		.mode = 0666,
// 	},
// //	.show  = switch_usb_show,
// };
 
static int demo_open(struct inode* inode, struct file* filp)
{
	struct demo_reg_dev* dev;
	dev = container_of(inode->i_cdev, struct demo_reg_dev, dev);
	filp->private_data = dev;
	
	return 0;
}
 
static int demo_release(struct inode* inode, struct file* filp)
{
	return 0;
}
 
static ssize_t demo_read(struct file* filp, char __user* buf, size_t count, loff_t* f_pos)
{
	ssize_t err = 0;
	struct demo_reg_dev* dev = filp->private_data;
 
	if (down_interruptible(&(dev->sem)))
	{
		return -ERESTARTSYS;
	}
 
	if (count < sizeof(dev->val))
	{
		goto out;
	}
 
	if (copy_to_user(buf, &(dev->val), sizeof(dev->val)))
	{
		err = -EFAULT;
		goto out;
	}
 
	err = sizeof(dev->val);
 
out:
	up(&(dev->sem));
	return err;
}
 
static ssize_t demo_write(struct file* filp, const char __user* buf, size_t count, loff_t* f_pos)
{
	struct demo_reg_dev* dev = filp->private_data;
	ssize_t err = 0;
	
	if (down_interruptible(&(dev->sem)))
	{
		return -ERESTARTSYS;
	}
 
	if (count != sizeof(dev->val))
	{
		err = -EFAULT;
		goto out;
	}
 
	if (copy_from_user(&(dev->val), buf, count))
	{
		err = -EFAULT;
		goto out;
	}
 
	err = sizeof(dev->val);
 
out:
	up(&(dev->sem));
	return err;
}
 
static ssize_t __demo_get_val(struct demo_reg_dev* dev, char* buf)
{
	int val = 0;
 
	if (down_interruptible(&(dev->sem)))
	{
		return -ERESTARTSYS;
	}
 
	val = dev->val;
	up(&(dev->sem));
 
	return snprintf(buf, PAGE_SIZE, "%d\n", val);
}
 
static ssize_t demo_val_show(struct device* dev, struct device_attribute* attr, char* buf)
{
	struct demo_reg_dev* hdev = (struct demo_reg_dev*)dev_get_drvdata(dev);
 
	return __demo_get_val(hdev, buf);
}
 
static ssize_t demo_val_store(struct device*dev, struct device_attribute* attr, const char* buf, size_t count){
    struct demo_reg_dev* hdev = (struct demo_reg_dev*)dev_get_drvdata(dev);
 
    return __demo_set_val(hdev, buf, count);
}
 
static void demo_create_proc(void)
{
	proc_create(DEMO_DEVICE_PROC_NAME, 0644, 0, &demo_proc_fops);
}
 
static void demo_remove_proc(void)
{
	remove_proc_entry(DEMO_DEVICE_PROC_NAME, NULL);
}
 
static int __demo_setup_dev(struct demo_reg_dev* dev)
{
	int err;
	dev_t devno = MKDEV(demo_major, demo_minor);
	
	memset(dev, 0, sizeof(struct demo_reg_dev));
 
	cdev_init(&(dev->dev), &demo_fops); // 初始化dev, 绑定对应的fops
	dev->dev.owner = THIS_MODULE;
	dev->dev.ops = &demo_fops;
	
	err = cdev_add(&(dev->dev), devno, 1); // 注册设备
	if (err)
	{
		return err;
	}
	
	sema_init(&(dev->sem), 1); // 初始化信号量为互斥量
	dev->val = 0;
 
	return 0;
}
 
static int __init demo_init(void)
{
	int err = -1;
	dev_t dev = 0;
	struct device* temp = NULL;
	
	printk(KERN_ALERT"Initializing demo device.\n");
 
	err = alloc_chrdev_region(&dev, 0, 1, DEMO_DEVICE_NODE_NAME); // 动态申请设备号,放入dev中
	if (err < 0)
	{
		printk(KERN_ALERT"Failed to alloc char dev region.\n");
        	goto fail;
	}
 
	demo_major = MAJOR(dev); // 得到主设备号
	demo_minor = MINOR(dev); // 得到次设备号
 
	demo_dev = kmalloc(sizeof(struct demo_reg_dev), GFP_KERNEL); // 为结构体demo_reg_dev分配内核空间
	
	if (!demo_dev)
	{
		err = -ENOMEM;
		printk(KERN_ALERT"Failed to alloc demo device.\n");
        	goto unregister;
	}
 
	err = __demo_setup_dev(demo_dev);	// cdev_init	cdev_add
 
	if (err)
	{
		printk(KERN_ALERT"Failed to setup demo device: %d.\n", err);
        	goto cleanup;
	}
 
	demo_class = class_create(THIS_MODULE, DEMO_DEVICE_CLASS_NAME); // 创建class注册到内核中
	if (IS_ERR(demo_class))
	{
		err = PTR_ERR(demo_class);
        	printk(KERN_ALERT"Failed to create demo device class.\n");
        	goto destroy_cdev;
	}
	
	temp = device_create(demo_class, NULL, dev, NULL, "%s", DEMO_DEVICE_FILE_NAME);// //在/dev目录下生成demo的设备文件
	if(IS_ERR(temp))
	{
        	err = PTR_ERR(temp);
        	printk(KERN_ALERT"Failed to create demo device.\n");
        	goto destroy_class;
    	}
 
	err = device_create_file(temp, &dev_attr_val); // 在/sys/class/xxx/xxx目录下创建属性文件val
	if (err < 0)
	{
		printk(KERN_ALERT"Failed to create attribute val of demo device.\n");
        	goto destroy_device;
	}
 
	dev_set_drvdata(temp, demo_dev);
	
	demo_create_proc();
 
	printk(KERN_ALERT"Succedded to initialize demo device.\n");
 
	return 0;
 
destroy_device:
    device_destroy(demo_class, dev);
destroy_class:
    class_destroy(demo_class);
destroy_cdev:
    cdev_del(&(demo_dev->dev));
cleanup:
    kfree(demo_dev);
unregister:
    unregister_chrdev_region(MKDEV(demo_major, demo_minor), 1);
fail:
    return err;
}
 
static void __exit demo_exit(void)
{
	dev_t devno = MKDEV(demo_major, demo_minor);
 
	printk(KERN_ALERT"Destroy demo device.\n");
 
	demo_remove_proc();	// 1. remove proc
 
	if (demo_class)
	{
		device_destroy(demo_class, MKDEV(demo_major, demo_minor));	// 2. device destroy
		class_destroy(demo_class);	// 3. class destroy
	}
 
	if (demo_dev)
	{
		cdev_del(&(demo_dev->dev)); //	4. delete cdev
		kfree(demo_dev);
	}
	
	unregister_chrdev_region(devno, 1);
}
 
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Demo Register Driver");
 
module_init(demo_init);
module_exit(demo_exit);

二、增加HAL层

1. 代码目录:

android/hardware/libhardware/include/hardware/demo.h
android/hardware/libhardware/modules/Android.mk
android/hardware/libhardware/modules/demo/Android.mk
android/hardware/libhardware/modules/demo/demo.cpp
android/system/core/rootdir/ueventd.rc

2. 代码

android/hardware/libhardware/include/hardware/demo.h

#ifndef ANDROID_INCLUDE_DEMO_H
#define ANDROID_INCLUDE_DEMO_H
 
#include <hardware/hardware.h>
__BEGIN_DECLS
 
 
/**
* The id of this module
*/
#define DEMO_HARDWARE_MODULE_ID "demo"
 
/**
* The id of this device
*/
#define DEMO_HARDWARE_DEVICE_ID "demo"
 
struct demo_module_t
{
	hw_module_t common;
};
 
struct demo_device_t
{
	struct hw_device_t common;
	int fd;
	int (*set_val)(struct demo_device_t* dev, int val);
	int (*get_val)(struct demo_device_t* dev, int* val);
};
 
__END_DECLS
 
#endif

android/hardware/libhardware/modules/demo/demo.cpp

#define LOG_TAG "demoHALStub"
 
#include <hardware/hardware.h>
#include <hardware/demo.h>
 
#include <fcntl.h>
#include <errno.h>
 
#include <cutils/log.h>
#include <cutils/atomic.h>
 
#include <malloc.h>
#include <stdint.h>
#include <sys/time.h>
 
#include <system/audio.h>
#include <hardware/audio.h>
 
 
 
#define DEVICE_NAME "/dev/demo"
#define MODULE_NAME "HardwareDemo"
#define MODULE_AUTHOR "likai@qq.com"
 
static int demo_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
static int demo_device_close(struct hw_device_t* device);
static int demo_set_val(struct demo_device_t* dev, int val);
static int demo_get_val(struct demo_device_t* dev, int* val);
 
static struct hw_module_methods_t demo_module_methods =
{
	.open = demo_device_open
};
 
struct demo_module_t HAL_MODULE_INFO_SYM =
{
	.common = 
	{
		.tag = HARDWARE_MODULE_TAG,
		.version_major = 1,
		.version_minor = 0,
		.id = DEMO_HARDWARE_MODULE_ID,
		.name = MODULE_NAME,
		.author = MODULE_AUTHOR,
		.methods = &demo_module_methods,
	}
};
 
static int demo_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device)
{
	if (!strcmp(id, DEMO_HARDWARE_DEVICE_ID))
	{
		struct demo_device_t* dev;
 
		dev = (struct demo_device_t*)malloc(sizeof(struct demo_device_t));
		if (!dev)
		{
			ALOGE("Failed to alloc space for demo_device_t.");
			return -EFAULT;
		}
 
		memset(dev, 0, sizeof(struct demo_device_t));
		
		dev->common.tag = HARDWARE_DEVICE_TAG;
		dev->common.version = 0;
		dev->common.module = (hw_module_t*)module;
		dev->common.close = demo_device_close;
		dev->set_val = demo_set_val;
		dev->get_val = demo_get_val;
 
		if ((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1)
		{
			ALOGE("Failed to open device file /dev/demo --%s", strerror(errno));
			free(dev);
			return -EFAULT;
		}
 
		*device = &(dev->common);
		
		ALOGI("Open device file /dev/demo successfully.");
 
		return 0;
	}
	
	return -EFAULT;
}
 
static int demo_device_close(struct hw_device_t* device)
{
	struct demo_device_t* demo_device = (struct demo_device_t*)device;
	if (demo_device)
	{
		close(demo_device->fd);
		free(demo_device);
	}
	
	return 0;
}
 
static int demo_set_val(struct demo_device_t* dev, int val)
{
	if (!dev)
	{
		ALOGE("Null dev pointer. demo_set_val");
		return -EFAULT;
	}
 
	ALOGI("Set val %d to device file /dev/demo.", val);
	write(dev->fd, &val, sizeof(val));
 
	return 0;
}
 
static int demo_get_val(struct demo_device_t* dev, int* val)
{
	if (!dev)
	{
		ALOGE("Null dev pointer. demo_get_val");
		return -EFAULT;
	}
 
	if (!val)
	{
		ALOGE("Null val pointer. demo_get_val");
		return -EFAULT;
	}
 
	read(dev->fd, val, sizeof(*val));
 
	ALOGI("Get value %d from device file.", *val);
 
	return 0;
}

android/hardware/libhardware/modules/demo/Android.mk

LOCAL_PATH := $(call my-dir)
 
# HAL module implemenation stored in
# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so
include $(CLEAR_VARS)
 
LOCAL_MODULE := demo.$(TARGET_BOARD_PLATFORM)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
#LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TAGS := debug
LOCAL_CFLAGS += -DQEMU_HARDWARE
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := demo.cpp
include $(BUILD_SHARED_LIBRARY)

android/hardware/libhardware/modules/Android.mk 中增加demo模块,非常重要。

三、增加JNI对HAL的访问

1. 文件目录

android/frameworks/base/services/core/jni/onload.cpp

android/frameworks/base/services/core/jni/Android.mk

android/frameworks/base/services/core/jni/com_android_server_DemoService.cpp

2. 代码

android/frameworks/base/services/core/jni/onload.cpp

 int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
 int register_dp_android_server_ActivityManagerService(JNIEnv* env);
 int register_android_server_DemoService(JNIEnv* env); //增加这一行
 
 };
 
@@ -92,6 +93,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
     register_android_server_HardwarePropertiesManagerService(env);
     register_dp_android_server_ActivityManagerService(env);
     register_android_server_mcu(env);
     register_android_server_DemoService(env); //增加这一行
 
 
     return JNI_VERSION_1_4;

android/frameworks/base/services/core/jni/Android.mk

# 增加下面一行
LOCAL_SRC_FILES += \
    ...
     $(LOCAL_REL_DIR)/com_android_server_DemoService.cpp  \
    ...

android/frameworks/base/services/core/jni/com_android_server_DemoService.cpp

#define LOG_TAG "DemoService"
 
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
 
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/demo.h>
 
#include <stdio.h>
 
namespace android
{
	static void demo_setVal(JNIEnv* env, jobject jclazz, jint ptr, jint value)
	{
		demo_device_t* device = (demo_device_t*)ptr;
		if (!device)
		{
			ALOGE("Device demo is not open. demo_setVal");
			return;
		}
	
		int val = value;
 
		ALOGI("Set value %d to device demo.", val);
 
		device->set_val(device, val);
	}
 
	static jint demo_getVal(JNIEnv* env, jobject clazz, jint ptr)
	{
		demo_device_t* device = (demo_device_t*)ptr;
		if (!device)
		{
			ALOGE("Device demo is not open. demo_getVal");
			return 0;
		}
 
		int val = 0;
		
		device->get_val(device, &val);
 
		ALOGI("Get value %d from device demo.", val);
 
		return val;
	}
 
	static inline int demo_device_open(const hw_module_t* module, struct demo_device_t** device)
	{
		return module->methods->open(module, DEMO_HARDWARE_DEVICE_ID, (struct hw_device_t**)device);
	}
 
	static jint demo_init(JNIEnv* env, jclass clazz)
	{
		demo_module_t* module;
		demo_device_t* device;
 
		ALOGI("Initializing HAL stub demo......");
 
		if (hw_get_module(DEMO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0)
		{
			ALOGI("Device demo found.");
			
			if (demo_device_open(&(module->common), &device) == 0)
			{
				ALOGI("Device demo is open.");
 
				return (jint)device;
			}
	
			ALOGE("Failed to open device demo.");
			return 0;
		}
		
		ALOGE("Failed to get HAL stub demo.");
		return 0;
	}
 
	static const JNINativeMethod method_table[] =
	{
		{"init_native", "()I", (void*)demo_init},
		{"setVal_native", "(II)V", (void*)demo_setVal},
		{"getVal_native", "(I)I", (void*)demo_getVal},
	};
 
	int register_android_server_DemoService(JNIEnv* env)
	{
		return jniRegisterNativeMethods(env, "com/android/server/DemoService", method_table, NELEM(method_table));
	}
};

四、增加系统服务

1. 文件目录

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

android/frameworks/base/services/core/java/com/android/server/DemoService.java

android/frameworks/base/services/java/com/android/server/SystemServer.java

android/frameworks/base/Android.mk

2. 代码

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

package android.os;
 
interface IDemoService {
        void setVal(int val);
        int getVal();
}

android/frameworks/base/services/core/java/com/android/server/DemoService.java

package com.android.server;
 
import android.content.Context;
import android.os.IDemoService;
import android.util.Slog;
 
public final class DemoService extends IDemoService.Stub {
        private static final String TAG = DemoService.class.getSimpleName();
 
        private int mPtr = 0;
 
        DemoService() {
                mPtr = init_native();
 
                if (mPtr == 0) {
                        Slog.e(TAG, "Fail to init IDemoService.");
                }
        }
 
        public void setVal(int val) {
                if (mPtr == 0) {
                        Slog.e(TAG, "IDemoService is not inited.");
                }
                setVal_native(mPtr, val);
        }
 
        public int getVal() {
                if (mPtr == 0) {
                        Slog.e(TAG, "IDemoService is not inited.");
                }
 
                return getVal_native(mPtr);
        }
 
        private static native int init_native();
        private static native void setVal_native(int ptr, int val);
        private static native int getVal_native(int ptr);
 
}

android/frameworks/base/services/java/com/android/server/SystemServer.java

// 在启动其他服务增加启动DemoService代码
 
public final class SystemServer {
                 } catch (Throwable e) {
                     reportWtf("Notifying NetworkScoreService running", e);
                 }
+
+               try {
+               Slog.i(TAG, "Demo service");
+               ServiceManager.addService("demo", new DemoService());
+       } catch (Throwable e) {
+               Slog.e(TAG, "Failure starting Demo service", e);
+       }
+
                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
             }
         });

android/frameworks/base/Android.mk

# 增加aidl的编译
LOCAL_SRC_FILES += \
        core/java/android/os/IUpdateLock.aidl \
        core/java/android/os/IUserManager.aidl \
        core/java/android/os/IVibratorService.aidl \
+       core/java/android/os/IDemoService.aidl \

最后,需要进行api更新:make update-api

五、增加SELinux权限

修改文件:

    modified:   android/device/softwinner/common/sepolicy/device.te
    modified:   android/device/softwinner/common/sepolicy/domain.te
    modified:   android/device/softwinner/common/sepolicy/file_contexts
    modified:   android/device/softwinner/common/sepolicy/service.te
    modified:   android/device/softwinner/common/sepolicy/service_contexts
    modified:   android/device/softwinner/common/sepolicy/system_app.te
    modified:   android/device/softwinner/common/sepolicy/system_server.te
    modified:   android/device/softwinner/common/sepolicy/untrusted_app.te

android/device/softwinner/common/sepolicy/device.te

type demo_device, dev_type;


android/device/softwinner/common/sepolicy/domain.te

allow domain demo_device:chr_file rw_file_perms;


android/device/softwinner/common/sepolicy/file_contexts

/dev/demo u:object_r:demo_device:s0


android/device/softwinner/common/sepolicy/service.te

type demo_service, system_api_service, system_server_service, service_manager_type;


android/device/softwinner/common/sepolicy/service_contexts

demo                                      u:object_r:demo_service:s0


android/device/softwinner/common/sepolicy/system_app.te

allow system_app demo_service:service_manager find;


android/device/softwinner/common/sepolicy/system_server.te

allow system_server demo_device:chr_file rw_file_perms;


android/device/softwinner/common/sepolicy/untrusted_app.te

allow untrusted_app demo_service:service_manager find;

 六、增加应用访问

在应用中增加服务的访问测试代码:

        IDemoService demoService = IDemoService.Stub.asInterface(ServiceManager.getService("demo"));
 
        try {
            int val = demoService.getVal();
            Log.d(TAG, "val = " + val);
 
            demoService.setVal(100);
 
            val = demoService.getVal();
            Log.d(TAG, "val = " + val);
        } catch (Exception e) {
            e.printStackTrace();
        }

参考:业精于勤毁于嬉_行者常至,为者常成_CSDN博客-Android,camera专题--MTK,MTK驱动总结领域博主


---------------------
作者:力开
来源:CSDN
原文:https://blog.csdn.net/weixin_42788930/article/details/122145682
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值