如何应对Android面试官->进程通信,启动与获取SM服务

Android Binder——SM添加服务解析(十一) 上一篇我们介绍到,C++ 层通过代理调用 addService 最终调用到了 ServiceManager.cpp 的 addService() 方法,这里我们继续分析。 阅读详情

前言


image.png

本章主要围绕 ServiceManager 进行讲解,通过注册、启动等流程,玩转 ServiceManager;

ServiceManager 是什么?


客户端想要调用系统的 AMS 或者 PMS,但是客户端直接调用不到,只能获取到 AMS-IBinder 对象,AMS 在启动的时候就会把 AMS-IBinder 对象注册到 ServiceManager 中,客户端只要找到 ServiceManager 之后,就可以把 AMS-IBinder 对象返回给客户端,客户端就可以通过 AMS-IBinder 同 AMS 进行通信;

ServiceManager 它也是一个服务,但是它的句柄是确认的,也就是 handle = 0,而我们客户端在调用系统服务的时候,只需要调用这个已知道的 handle = 0 的服务就可以获取到 ServiceManager 的代理对象,然后就可以 同其他系统服务通信了;

ServiceManager 服务是如何注册的?


那么 ServiceManager 是怎么注册的呢?我们进入源码看一下,它的注册就在 init.rc 中:

// 打开 ServiceManager 这个程序,就会 调用 ServiceManager 的 main 方法
service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

打开 ServiceManager 这个程序,就会 调用 servicemanager.c 的 main 方法,我们进入 main 方法看下:

int main(int argc, char **argv)
{
    struct binder_state *bs;
    // 这个方法做了两件事情,一是 open 方法,打开 binder 驱动,二是 mmap 方法,建立映射关系
    bs = binder_open(128*1024);
    if (!bs) {
        ALOGE("failed to open binder driver\n");
        return -1;
    }
    // 把 ServiceManager 设置成服务大管家
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    selinux_enabled = is_selinux_enabled();
    sehandle = selinux_android_service_context_handle();
    selinux_status_open(true);

    if (selinux_enabled > 0) {
        if (sehandle == NULL) {
            ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
            abort();
        }

        if (getcon(&service_manager_context) != 0) {
            ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
            abort();
        }
    }

    union selinux_callback cb;
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    // 循环监听有没有其他服务访问
    binder_loop(bs, svcmgr_handler);

    return 0;
}

binder_open 这个方法做了两件事情,一是 open(128*1024) 方法,打开 binder 驱动,二是 mmap 方法,建立映射关系;

binder_become_context_manager 把 ServiceManager 设置成服务大管家;

binder_loop 循环监听有没有其他服务访问;

我们进入 binder_become_context_manager 这个方法看下,它是如何成为大管家的

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

直接调用的 ioctl 方法,传入的参数是 BINDER_SET_CONTEXT_MGR,而 这个方法最终调用到 binder_ioctl 中,我们进入 BINDER_SET_CONTEXT_MGR 这个 case 看下:

case BINDER_SET_CONTEXT_MGR:
    ret = binder_ioctl_set_ctx_mgr(filp);
    if (ret)
	goto err;
    break;

这个 case 中调用的是 binder_ioctl_set_ctx_mgr,我们进入这个方法看下:

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	struct binder_context *context = proc->context;

	kuid_t curr_euid = current_euid();
   // 先判断是不是已经存在了,存在则直接返回
	if (context->binder_context_mgr_node) {
		pr_err("BINDER_SET_CONTEXT_MGR already set\n");
		ret = -EBUSY;
		goto out;
	}
	ret = security_binder_set_context_mgr(proc->tsk);
	if (ret < 0)
		goto out;
   // 判断 sm 的 uid 是否是有效的,第一次肯定是无效的,所以直接进入 else
	if (uid_valid(context->binder_context_mgr_uid)) {
		if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
			pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
			       from_kuid(&init_user_ns, curr_euid),
			       from_kuid(&init_user_ns,
					 context->binder_context_mgr_uid));
			ret = -EPERM;
			goto out;
		}
	} else {
            // 设置 uid
            context->binder_context_mgr_uid = curr_euid;
	}
        // 通过 binder_new_node 创建 SM 的 binder_context_mgr_node 结构体
	context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
	if (!context->binder_context_mgr_node) {
		ret = -ENOMEM;
		goto out;
	}
	context->binder_context_mgr_node->local_weak_refs++;
	context->binder_context_mgr_node->local_strong_refs++;
	context->binder_context_mgr_node->has_strong_ref = 1;
	context->binder_context_mgr_node->has_weak_ref = 1;
out:
	return ret;
}

到这里,就把 ServiceManager 设置成了大管家;

我们接下来看下 binder_loop 这个方法:

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    // 结构体,这个结构体中有读和写,以及buffer
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    // BC_ENTER_LOOPER 是一个命令,进入循环的命令
    readbuf[0] = BC_ENTER_LOOPER;
    // 执行 binder_write 把 buffer 放进去,内部执行 ioctl 方法,最终调用到 binder_ioctl_write_read 方法
    binder_write(bs, readbuf, sizeof(uint32_t));

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }

        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

// 执行 binder_write 把 buffer 放进去,内部执行 ioctl 方法,最终调用到 binder_ioctl_write_read 方法,我们进入这个方法看下:

static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;
	struct binder_write_read bwr;

	if (size != sizeof(struct binder_write_read)) {
		ret = -EINVAL;
		goto out;
	}
	if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
	binder_debug(BINDER_DEBUG_READ_WRITE,
		     "%d:%d write %lld at %016llx, read %lld at %016llx\n",
		     proc->pid, thread->pid,
		     (u64)bwr.write_size, (u64)bwr.write_buffer,
		     (u64)bwr.read_size, (u64)bwr.read_buffer);
    // 两个 if 来判断是要执行 read 还是 write 方法
	if (bwr.write_size > 0) {
		ret = binder_thread_write(proc, thread,
					  bwr.write_buffer,
					  bwr.write_size,
					  &bwr.write_consumed);
		trace_binder_write_done(ret);
		if (ret < 0) {
			bwr.read_consumed = 0;
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
	if (bwr.read_size > 0) {
		ret = binder_thread_read(proc, thread, bwr.read_buffer,
					 bwr.read_size,
					 &bwr.read_consumed,
					 filp->f_flags & O_NONBLOCK);
		trace_binder_read_done(ret);
		if (!list_empty(&proc->todo))
			wake_up_interruptible(&proc->wait);
		if (ret < 0) {
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
	binder_debug(BINDER_DEBUG_READ_WRITE,
		     "%d:%d wrote %lld of %lld, read return %lld of %lld\n",
		     proc->pid, thread->pid,
		     (u64)bwr.write_consumed, (u64)bwr.write_size,
		     (u64)bwr.read_consumed, (u64)bwr.read_size);
	if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
out:
	return ret;
}

bwr.write_size > 0 执行 write 或者 bwr.read_size > 0 执行 read 操作,我们进入 write 的操作看下,这个方法最终执行的命令是我们前面看到的 BC_ENTER_LOOPER 这个命令,我们直接进入这个 case 看下

case BC_ENTER_LOOPER:
    binder_debug(BINDER_DEBUG_THREADS,
				     "%d:%d BC_ENTER_LOOPER\n",
				     proc->pid, thread->pid);
    if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
        thread->looper |= BINDER_LOOPER_STATE_INVALID;
	binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
	proc->pid, thread->pid);
    }
    // 其实就做了一件事情,给当前 thread 的 looper 设置了一个状态
    thread->looper |= BINDER_LOOPER_STATE_ENTERED;
    break;

其实就做了一件事情,给当前 thread 的 looper 设置了一个状态;

我们接着回到 binder_loop 方法往下看,下面一个是 for(;😉 无限循环,这里依然调用的是 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); 这个方法,只是 case 传递了 BINDER_WRITE_READ,我们继续进入这个方法看下,最终进入的是 binder_thread_read 方法,这个方法 最终调用到 ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); 进入等待状态了

if (wait_for_proc_work) {
	if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
					BINDER_LOOPER_STATE_ENTERED))) {
			binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n",
				proc->pid, thread->pid, thread->looper);
			wait_event_interruptible(binder_user_error_wait,
						 binder_stop_on_user_error < 2);
	}
	binder_set_nice(proc->default_priority);
	if (non_block) {
		if (!binder_has_proc_work(proc, thread))
                    ret = -EAGAIN;
	} else {
            // binder 是阻塞的,所以最终进入到这里
           ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
        }
}

SM 的注册还是比较简单的,一共就三步,第一步是 binder_open,第二步是 binder_become_context_manager(成为大管家,也叫设置为守护进程),第三步是 binder_loop

image.png

整体就是图上 1 、2 、3 三个步骤;

ServiceManager 服务是如何获取的


我们接下来看下 SM 是如何获取的;获取 SM 的情况一般是两种,一种是注册服务到 SM,一种是通过 SM 去获取服务,这两种都需要拿到 SM 才能实现;SM 的 native 层获取一般是在 IServiceManager.cpp 中,我们进入这个文件看下

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    
    return gDefaultServiceManager;
}

native 层的获取 SM 都是从这里执行的,首先是一个单例,然后一个 while 循环来获取 SM,如果获取不到会进行一个休眠等待,主要的获取逻辑在下面这行代码

gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));

self()

我们来逐步分析这个方法都做了什么,首先我们来看下 ProcessState::self()->getContextObject(NULL) 这个参数都做了什么操作;

首先是 self 方法,我们进入这个 self 方法看下做了什么

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}

很简单,就是 new ProcessState 并返回,创建这个 ProcessState 的时候做了什么呢?我们进入构造函数看一下

ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted{
        if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }

    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
    }

第一步,是 open_driver() 通过 open 方法打开 binder 设备(驱动),并通过 maxThreads 设置最大线程数;

static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);
    if (fd >= 0) {
        fcntl(fd, F_SETFD, FD_CLOEXEC);
        int vers = 0;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
            ALOGE("Binder driver protocol does not match user space protocol!");
            close(fd);
            fd = -1;
        }
        size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        if (result == -1) {
            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
        }
    } else {
        ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
    }
    return fd;
}

我们接着看创建的逻辑,

mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);

通过 mmap 设置共享内存, 1M-8K(普通服务的大小);可以看到有一个常量参数 BINDER_VM_SIZE 它的值就是 #define BINDER_VM_SIZE ((110241024) - (4096 *2));

所以这个 self() 方法做了三件事情,分别是:

  1. 打开驱动,open_driver() 方法;
  2. 设置最大线程数据,15;
  3. mmap,设置共享内存大小,1M-8K;

getContextObject(NULL)

我们接下来看下 getContextObject(NULL) 做了些什么?这个方法直接调用了

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

这个方法直接调用了 getStrongProxyForHandle(0) 传入了 0;我们进入这个方法看下都发生了什么?

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);
    // 查找 handle 对应的资源线
    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                Parcel data;
                // 测试 binder 是否已经准备就绪
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
            // 创建 BpBinder 并返回;
            b = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

这个方法就做了一件事情,就是创建 BpBinder,我们可以看下,在创建这个 BpBinider 的时候做了什么?

BpBinder


BpBinder::BpBinder(int32_t handle)
    : mHandle(handle)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);

    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    // 主要是调用 incWeakHandle 做一个弱引用的计数
    IPCThreadState::self()->incWeakHandle(handle);
}

所以 ProcessState::self()->getContextObject(NULL) 这个参数本质就是返回 BpBinder 对象;这个 BpBinder 对象就是指向服务端的代理对象,我们在这里就可以认为是 ServiceManager;

image.png

BpBinder 是 BBinder 的代理对象,这里可以认为它俩是一样的;

Java 层和 Native 层通信的时候,需要一个 BinderProxy 和 ServiceManagerProxy,ServiceManagerProxy 里面有一个 BinderProxy,这个 BinderProxy 指向 BpBinder,也就是说 Java 层需要拿到这个 BpBinder 才能通信,Java 通过操作 ServiceManagerProxy,而 ServiceManagerProxy 通过操作 Remote(BinderProxy),这个时候,这个 BinderProxy 它是 Java 层的,它要指向 Native 层的才能通信,而这个 Native 层的就是 BpBinder,这个 BpBinder 就是代理对象;

Native 层也需要获取 BpBinder,但是它与 Java 层的获取方式不同,它是通过操作 BpServiceManager ,而 BpServiceManager 可以直接操作 BpBinder,这就少了一步,Java 层和 Native 层都是 BpBinder,再由 BpBinder 进行 transac 进行通信

interface_cast

我们接下来看下这个 interface_cast 方法,这个方法传入的参数就是 BpBinder;

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

这个方法调用的是 INTERFACE::asInterface(obj),这里是一个泛型操作,所以最终调用的是 IServiceManager 的 asInterface(obj) 方法; 但是这个方法并不在 IServiceManager 这个文件中,它使用的模板放进行了一个宏定义,我们在找下这个宏定义

#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();

这里通过宏定义之后会进行一个替换,替换之后的 C++ 代码实现如下:

static const android::String16 descriptor;

static android::sp< IServiceManager > asInterface(const

android::sp<android::IBinder>& obj)

virtual const android::String16& getInterfaceDescriptor() const;

IServiceManager ();

virtual ~IServiceManager();

该过程主要是声明asInterface(),getInterfaceDescriptor()方法,所以这里我们不用过多的关心;

我们接着看下面的宏定义以及替换之后的逻辑

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \

替换之后的逻辑如下:

const android::String16
IServiceManager::descriptor(“android.os.IServiceManager”);
const android::String16& IServiceManager::getInterfaceDescriptor() const
{
    return IServiceManager::descriptor;
}

android::sp<IServiceManager> IServiceManager::asInterface(const
android::sp<android::IBinder>& obj)
{
    android::sp<IServiceManager> intr;
    if(obj != NULL) {
        intr = static_cast<IServiceManager *>(
        obj->queryLocalInterface(IServiceManager::descriptor).get());
        if (intr == NULL) {
            // 等价于 new BpServiceManager(BpBinder)
            intr = new BpServiceManager(obj);
        }
    }
    return intr;
}

IServiceManager::IServiceManager () {}
IServiceManager::~ IServiceManager() {}

展开之后,我们主要关心的就是 asInterface 方法,这个方法具体做了什么,我们来看下:

这里就是创建了 BpServiceManager,构造方法中传入的 BpBinder,我们来看下它是怎么使用 BpBinder 的;

BpServiceManager


BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }

我们来看下 BpInterface

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);

protected:
    virtual IBinder*            onAsBinder();
};

这里我们直接看 BpInterface 的继承 BpRefBase

BpRefBase::BpRefBase(const sp<IBinder>& o)
    : mRemote(o.get()), mRefs(NULL), mState(0)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);

    if (mRemote) {
        mRemote->incStrong(this);           // Removed on first IncStrong().
        mRefs = mRemote->createWeak(this);  // Held for our entire lifetime.
    }
}

到此,我们就找到了 mRemote 它来操作 BpBinder,也就是对应的 java 层调用 mRemote.transact() 的时候,就是调用的 BpBinder.transact() 来进行通信;

所以这个方法也是做了三件事情

  1. new BpServiceManager(new BpBinder)
  2. remote.transcat() 远程调用
  3. remote = BpBinder

到此,ServiceManager 的注册与获取就写完了;

下一章预告


进程通信注册与获取服务及线程池讲解

欢迎三连


来都来了,点个关注点个赞吧,你的支持是我最大的动力~~~

Win10安全模式下修改C盘权限的完整指南(附解决开始菜单失灵方案) 本文提供了一份在Win10安全模式下修复因C盘权限误改导致开始菜单失灵的完整指南。文章深入剖析了权限体系,特别是TrustedInstaller角色的重要性,并详细讲解了通过安全模式进入系统、使用ICACLS命令或图形界面精准重置C盘关键目录权限的步骤,最终恢复系统核心功能。 阅读详情

相关推荐

Xilinx SDK工程编译、链接、调试注意事项

1、添加工程 启动SDK时选取workspace,如下图所示: 对应的workpace文件夹中会生成如下几个文件: 如果当前workspace是首次加载工程,SDK会进入启动界面 点击Import Project,导入已有工程 点击Browse,添加工程路径,一个完整的工程包含一个bsp文件夹,一个HW文件夹,和一个src文件夹(添加C功能函数) 点击Fini...

Autumn_He的博客 1万+

native层的c++如何通过binder获取sm服务(四)

前几篇Binder相关文章讲了驱动的注册和提供的接口,以及ServiceManager的启动运行,很自然接下来我们需要思考客户端进程应该如何获取到sm提供的服务(注册服务和查询服务),本篇从native层c++角度继续分析。 我们需要时刻牢记,应用程序跨进程只能依靠某种介质(比如socket是靠网卡、binder是靠驱动、内存共享是靠物理内存),直接软件去架构是无法做到的,在分析ServiceManager时也说了,sm是一个标准的Binder Server,因之,客户端进程想要访问ServiceManag

dream_caoyun的博客 1451

大漠多线程增强版:解决窗口消失、坐标错乱与识别卡死

多线程环境下窗口自动化面临的核心挑战,本质是GDI资源非线程安全、HWND生命周期失控及图像引擎状态污染三大底层问题。Windows GUI编程中,跨线程共享hDC、缓存失效的窗口句柄、OpenCV静态Mat等设计缺陷,导致FindPic失败、GetCursorPos异常、坐标跳变等典型故障。本方案通过线程局部存储(TLS)重构资源生命周期、WinEventHook+心跳探测构建实时窗口状态链、ABI对齐的多语言内存契约,实现稳定低延迟的并发UI识别与控制。适用于游戏多开监控、电商RPA、后台UI状态采集等

weixin_30561177的博客 477

基于sm社区公益服务平台(JSP+java+springmvc+mysql+MyBatis)

平台可以为志愿者和受助者搭建更加便捷、高效的交流渠道,同时也可以帮助公益组织更好地管理和利用自己的资源。通过该平台,我们可以共同推动公益事业的发展,为社会做出更多的贡献。+ Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。用户发送请求:用户通过浏览器或其他客户端向系统发送请求,请求访问特定的页面或执行特定的操作。前端展示结果:前端接收到后端返回的数据,根据需要进行展示。或者 Mac OS;后端程序接收到请求后,根据请求的类型和参数进行相应的处理。

菲林计算机毕设 1万+

WordPress主题开发实战:从零开始手把手教你创建自定义主题(含常用函数大全)

本文是一份WordPress主题开发实战指南,从零开始手把手教你创建自定义主题。内容涵盖主题核心文件结构、模板层级与循环机制、函数与钩子的运用,并附有详细的常用函数大全,帮助开发者彻底掌握主题开发的核心技能,实现从使用者到创造者的转变。

weixin_27943395的博客 256

【紫光同创PG2L200H开发板】盘古200Pro+开发板(MES2L676-200HP)

开发板 SMA接口符合标准 SMA 接口规范,可使用”内螺旋+插针“的 SMA 线连接板卡”外螺旋+插孔”的 SMA 接口进行通信,板卡 SMA 接口与 FPGA 的 HSST 收发器的一路 RX/TX 相连,TX信号和 RX 信号都是以差分信号方式通过隔直电容连接 FPGA 和 SMA 接口, TX 发送和 RX 接收数据速率高达 6.6Gb/s。为了减轻带电插拔造成对 FPGA 芯片的损坏,在设计上在 JTAG 信号位置添加了保护二极管来保证信号的电压在 FPGA 接受的范围,避免 FPGA 的损坏。

MYMINIEYE的博客 2884

Android 12 系统源码分析 | Native Binder 代码变迁

作者:秋城 注:广义上 Native Binder 可理解为包含 vnd,hw,rpc 等内容,本文所讨论的Native Binder 指的仅是 servicemanager 服务程序及 libbinder 中相关代码,不做广义的延伸 一、前言 Servicemanager 程序(以下简称SM)是 Android 系统 binder 模块重要的组成部分,扮演着类似 C/S 架构中的 DNS 服务器的角色,提供服务增查和权限管理等功能支撑 在 Android 11 之前的版本里,SM 是面向 bind.

m0_62167422的博客 2348

UDS $27服务DLL文件生成

UDS 27算法 DLL文件生成

hkj8808的博客 1282

android 系统后台进程数限制

开发项目遇到一个问题,打开10个左右的应用后,再打开其他应用就会报错 01-01 12:04:25.937 3958 3958 E ProcessState: Using /dev/binder failed: unable to mmap transaction memory. 01-01 12:04:25.938 3958 3958 F ProcessState: Binder d...

qq_34694875的博客 6216

滚动时域优化方法

滚动时域优化(Receding Horizon Optimization, RHO),也被广泛称为模型预测控制(Model Predictive Control, MPC),是一种用于动态系统优化和控制的。其核心思想是“在每个当前时刻k,控制器只考虑未来一个(称为“预测时域”或“优化时域”,长度N)内的系统行为。(通过测量或估计得到)。(描述状态如何随输入和干扰变化)。(如果有,可能基于历史数据或假设)。在这个有限的未来时域[k, k+N-1]上,求解一个。这个优化问题的目标是找到一个。

m0_69522810的博客 2293

YOLO 系列专栏(二十九)YOLO26 × StarNet 轻量化涨点方案(CVPR 2024)轻量化、主干改进篇 | 星星卷积虽小,却蕴藏着巨大的高效潜力

StarNet 出自 CVPR 2024 轻量化主干网络工作,核心创新是,依靠逐元素乘法实现高阶特征融合,在几乎不增加参数量与 FLOPs 的前提下,大幅提升特征表达能力,天然适合等多任务统一建模。本文将 StarNet 完整替换 YOLO26 原有主干,保留 YOLO26 无 NMS 检测头与轻量化结构,实现,并提供。

qq_36130719的博客 410

安卓开发kotlin推荐书籍,作为字节跳动面试官

自我介绍一下,小编13年北京科技大学毕业,曾经在字节跳动待过,也去过vivo、小米等大厂,18年进入华为一直到现在。小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担,能在岗位上再创辉煌,步步高升!!。

2401_84904043的博客 1144

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

骚年!用Binder原理去彻底征服Android大厂面试官吧

前言 在学习ServiceManager中的Binder机制过程中,有一个问题由于篇幅问题没有讲完,那就是MediaPlayerService是如何注册的。通过了解MediaPlayerService是如何注册的,可以得知系统服务的注册过程。 1.从调用链角度说明MediaPlayerService是如何注册的 我们先来看MediaServer的入口函数,代码如下所示。frameworks/...

weixin_44339238的博客 622

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

那些年Android面试官常问的知识点,成功入职腾讯

一、开始的开始 **Android框架体系架构(高级UI+FrameWork源码)**这块知识是现今使用者最多的,我们称之Android2013~2016年的技术,但是,即使是这样的技术,Android开发者也往往因为网上Copy代码习惯了而导致对这块经常“使用”的代码 **熟悉而又陌生:**熟悉的是几乎天天在和它们打交道,天天在复制这些代码;陌生的是虽然天天和这些代码打交道,但是并没有深入研究过这些代码的原理,代码深处的内涵。 所以我们需要从新的角度去分析这些知识点,深入研究他们,要学习源码,模仿源码,.

270

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

骚年!用Binder原理彻底征服大厂面试官吧

作者: 刘望舒原文:http://liuwangshu.cn/framework/binder/3-addservice.html这是后厂村码农的第131篇原创分享前言在上一篇文章中...

singwhatiwanna 482

看完吊打面试官!Android开发者应该会哪些东西才不会被公司淘汰?Android岗

前言 九月裸辞从长沙跑到上海,跑了一个月的面试,本月中旬终于拿到了爱奇艺的高级工程师offer。 做Android开发整4年有余,但是这一年才是最充实的,我花一年时间努力,送给了自己一个完美的蜕变! 寒冬裁员潮给我的危机感 我普通本科毕业,在长沙待了四年,其中只换过两家公司。去年这个时候,公司业绩不好要裁人。主管平时跟我关系比较好,说我本来也在被裁的考率名额中的,他帮我跟领导说了一堆好话才没事。我请主管吃了一顿火锅。 但是我无法像以前那么心安理得混日子了。因为我去年首付买了房。原本想着每个月稳定税后拿个8k

167

网易资深安卓架构师:2021年Android常见面试题,面试必问

开头 让我们一起来看看,字节跳动的第三面,面试官都问了什么?(第一二面的题目及答案已整理,需要的可以在文末领取) 从七月中旬开始,我前前后后差不多一共投递了八十份简历,到目前为止,我参加面试的面试有十二家(收到的面试通知) 字节跳动的面试我准备了一个多星期吧,因为比较重视。 从字节跳动面试官这些问题,可以看出,第三面他们考察的一个很重要的方向就是项目架构和性能优化。 “懒洋洋”甚至跟我吐槽:“我靠,现在大厂面试java后端都这么难的吗?” 大厂面试就是难,但是大厂的待遇是真的香!最近不是都在讨论华为的工资到

m0_52308677的博客 293

android热更新,我的Android美团求职之路,offer拿到手软

开头 昨天去面了一家公司,价值观有受到冲击。 面试官技术方面没的说,他可能是个完美主义的人,无论什么事情到了他那里好像都有解决的方案,我被说的无所适从,感觉他很厉害。 但我不能认可的是,面试官觉得加班是理所当然的,还说有人想把事情做的更好,愿意自主加班。这个我不否认,但凭什么说加班说的那么理所当然,违背国家制度也这么理所当然吗? 他问我对加班的看法。我说可以加班,有意义就可以。他继而反问,有些人觉得给老板打工就是没意义的,那是否所有加班都是没意义的呢?我当然说不是,他这么以己度人,我无语了。 我又说,忙的时

m0_56146626的博客 158

大厂offer手到擒来,写给正在求职的安卓开发

前言 跳槽容易,但想拿大厂的offer可不那么容易。很多小伙伴一直叨叨要跳槽,大大小小的公司面试了很多,但却很难拿到一个满意的offer,要么package太低,要么就是面试被虐。经过多次面试失利之后,方能明白什么叫基础不牢,地动山摇。面试官随便针对一个知识点深入考察一下,就回答不出来,这样还怎么能通过面试? 那么大厂到底面试到底需要哪些技术功底呢?这个就多了,不同公司的技术要求也不一样,但是相同的点在于,大公司对于技术的要求都不会很表面,必然会在一定广度的基础上要求有一定的深度。 1、一定要把基本的数据

m0_57079376的博客 192

2024年apk开发语言!分享Android资深架构师的成长之路,终获offer,网易软件测试面试题

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。欢迎大家一起交流讨论啊~网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化学习资料的朋友,可以戳这里获取一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2401_84556681的博客 848

干了5年Android开发还没掌握binder机制?工作感悟

反思 昨晚去北京大望路阿里面试, 产生了严重的挫败感, 羞愧难当. 比不得从大学就有目标有理想, 一直在为目标努力学习技术的同学, 在大学唯一能拿得出手的就是参加了电子设计大赛, 学了点嵌入式的知识. 毕业后开始做android, 说得好听点叫做项目, 实际上就是搬代码, 真正记到脑子里的有多少呢? 从百度Google搬到自己的代码里, 同一个问题要遇到好几次才能记住, 很多问题搬完了还不知道为什么这么做. ReactNative都做完一个项目了, 生命周期都记不清楚, 这样的工作有什么意义呢? 进步太慢,

m0_56259669的博客 141

快速学会!成功跳槽百度工资从15K涨到28K,完整PDF

前言 准备面试其实已经准备了挺久了,当时打算面试准备了差不多以后,跟公司谈谈涨薪的事情,谈不拢的话,就年后直接找其他的公司。谁想到婚假还没休完,老板就在公司宣布了撤出上海的决定,愿意去深圳的就去,不愿意的就自己找工作,给了一个多月的时间找工作,就没有赔偿了,反正公司也没钱。悲哀…. 幸好提前准备过,不过都是断断续续的今天看点这个,明天看点那个,三天打鱼两天晒网的,也没用心去准备.这下好了,直接到了最差的地步了,没办法,用心准备吧。 1、一定要把基本的数据结构,经典的算法,Unix编程,程序编译链接及计算机原

m0_52308677的博客 874

android binder机制---ProcessState和线程池

3, ProcessState和线程池      Android系统启动完成后,ActivityManager, PackageManager等各大服务都运行在system_server进程, app应用需要使用系统服务都是通过binder来完成进程之间的通信。      对于binder线程是如何管理的呢,又是如何创建的呢?其实无论是system_server进程,还是app进程, 都是

Jack的博客 1万+

分析 Package manager has died

这是今天遇到的一个issue,由于Binder造成的。虽然比较简单,还是保持记录下吧。先来开看一下Crash log:E/HpnsService(24810): HPNS Version is 5.0java.lang.RuntimeException: Package manager has died E/HpnsService(24810): at android.app.Applicati...

XXOOYC的专栏 1万+

android6.0 sm进程(获取MountService信息)

一、编译 我们先来看看sm的Android.mk # Copyright 2015 The Android Open Source Project # LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_MODULE := sm inc

kc58236582的博客 4089
上一篇: Jetpack Compose -> 无状态、状态提升,单向数据流
下一篇: 如何应对Android面试官->进程通信如何注册与获取服务
老A说
博客等级 码龄14年 1181粉丝 85原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值