Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver

在Android中实现Hook机制的实验 写过windows编辑的同学肯定对当时的特别强大的HOOK记忆犹新,不管是什么系统事件,都能捕捉住,那么在android中,如何实现Hook机制呢。 hook,又叫钩子,通常是指对一些方法进行拦截。这样当这些方法被调用时,也能够执行我们自己的代码,这也是面向切面编程的思想(AOP) android中,本身并不提供这样的拦截机制,但是有时候,我们可以在一些特殊的场合实现一种的Hook方法。 大致 阅读详情

Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver

方案猜测

同 Activity 一样,Client App 在 Menifest 中注册的静态广播外部 AMS 是无法知晓的,经过前几章的分析,相信大家已经是老司机了,我们可以先尝试提出自己的观点。

  1. 和 Activity 一样使用 Stub 组件占坑?仔细想一想是无法实现的,因为你无法预先确定 Client App 中广播的 Intent Filter。
  2. 使用动态注册即 context.registerBroadcastReceiver 代替静态注册,这确实是个好主意,但是重点在于注册的时机。我们需要在 VAService 启动时就预先把 VA 中所有安装的 Client App 的静态 Receiver 提前注册好,事实上外部 AMS 也是这么做的。不然的话,没有打开的 App 就无法收到广播了。

VA 静态广播注册

从前面我们知道,VAService 的启动时机实在 BinderProvider.onCreate():

@Override
    public boolean onCreate() {
        .....................
        VAppManagerService.get().scanApps();
        .....................
        return true;
    }

看到 VAppManagerService.get().scanApps()————>PersistenceLayer.read()——————->PackagePersistenceLayer.readPersistenceData()——————>VAppManagerService.loadPackage()———->VAppManagerService.loadPackageInnerLocked()————–>BroadcastSystem.startUp();

// 静态 Receiver 的注册
    public void startApp(VPackage p) {
        PackageSetting setting = (PackageSetting) p.mExtras;
        // 遍历 Client App 的 Receiver
        for (VPackage.ActivityComponent receiver : p.receivers) {
            ActivityInfo info = receiver.info;
            // 得到对应 Client App 在 VAService 中的记录列表
            List<BroadcastReceiver> receivers = mReceivers.get(p.packageName);
            if (receivers == null) {
                receivers = new ArrayList<>();
                mReceivers.put(p.packageName, receivers);
            }
            // 注册显式意图
            String componentAction = String.format("_VA_%s_%s", info.packageName, info.name);
            IntentFilter componentFilter = new IntentFilter(componentAction);
            BroadcastReceiver r = new StaticBroadcastReceiver(setting.appId, info, componentFilter);
            mContext.registerReceiver(r, componentFilter, null, mScheduler);
            // 推入记录
            receivers.add(r);
            // 遍历注册隐式意图
            for (VPackage.ActivityIntentInfo ci : receiver.intents) {
                IntentFilter cloneFilter = new IntentFilter(ci.filter);
                SpecialComponentList.protectIntentFilter(cloneFilter);
                r = new StaticBroadcastReceiver(setting.appId, info, cloneFilter);
                mContext.registerReceiver(r, cloneFilter, null, mScheduler);
                // 推入记录
                receivers.add(r);
            }
        }
    }

这里对每个 Client App 静态 Receiver 的信息使用统一的代理 StaticBroadcastReceiver 注册。

  1. 首先注册 Receiver 的显式意图,每个显式意图被重定向成格式为 “_VA_PKGNAME_CLASSNAME”的 componentAction 这么做的理由实际是真正注册是 VAService 进程空间的 StaticBroadcastReceiver 代理 Receiver,而不是 VA Client App 进程空间,所以直接注册 VA Client App 中的真实类名是没有意义的,这样通过 VAService 代理然后再从 Intent 中取出的 “_VA_PKGNAME_CLASSNAME”到 VA Client 中找到真正的 Receiver,这个逻辑和 Activity 的处理有些相似。
  2. 然后就是遍历 Intent-Filter,每个 Intent-Filter 注册一个 StaticBroadcastReceiver 代理。

这样我们的代理 Receiver 注册完毕了。

下面当代理 Receiver 收到广播时:

 @Override
        public void onReceive(Context context, Intent intent) {
            if (mApp.isBooting()) {
                return;
            }
            if ((intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) != 0 || isInitialStickyBroadcast()) {
                return;
            }
            String privilegePkg = intent.getStringExtra("_VA_|_privilege_pkg_");
            if (privilegePkg != null && !info.packageName.equals(privilegePkg)) {
                return;
            }
            PendingResult result = goAsync();
            if (!mAMS.handleStaticBroadcast(appId, info, intent, new PendingResultData(result))) {
                result.finish();
            }
        }

然后看到 handleStaticBroadcast

boolean handleStaticBroadcast(int appId, ActivityInfo info, Intent intent,
                                  PendingResultData result) {
        // 这里是取出真正的目标 Intent
        Intent realIntent = intent.getParcelableExtra("_VA_|_intent_");
        // 取出真正的目标 component
        ComponentName component = intent.getParcelableExtra("_VA_|_component_");
        // 用户 id
        int userId = intent.getIntExtra("_VA_|_user_id_", VUserHandle.USER_NULL);
        if (realIntent == null) {
            return false;
        }
        if (userId < 0) {
            VLog.w(TAG, "Sent a broadcast without userId " + realIntent);
            return false;
        }
        int vuid = VUserHandle.getUid(userId, appId);
        return handleUserBroadcast(vuid, info, component, realIntent, result);
    }

注意这里取出了真正的 Intent,和 Activity 类似,但是和 Activity 处理不同的是现在的逻辑还在 VAService 中:

然后 handleUserBroadcast()———–>handleStaticBroadcastAsUser()————>performScheduleReceiver():

private void performScheduleReceiver(IVClient client, int vuid, ActivityInfo info, Intent intent,
                                         PendingResultData result) {

        ComponentName componentName = ComponentUtils.toComponentName(info);
        BroadcastSystem.get().broadcastSent(vuid, info, result);
        try {
            // 远程调用 client app 的 scheduleReceiver
            client.scheduleReceiver(info.processName, componentName, intent, result);
        } catch (Throwable e) {
            if (result != null) {
                result.finish();
            }
        }
    }

client.scheduleReceiver() 这时候远程调用了 Client App 的 scheduleReceiver。这样我们回到了 Client App 进程空间:

@Override
    public void scheduleReceiver(String processName, ComponentName component, Intent intent, PendingResultData resultData) {
        ReceiverData receiverData = new ReceiverData();
        receiverData.resultData = resultData;
        receiverData.intent = intent;
        receiverData.component = component;
        receiverData.processName = processName;
        sendMessage(RECEIVER, receiverData);
    }

跟到消息队列中:

case RECEIVER: {
     handleReceiver((ReceiverData) msg.obj);
}
private void handleReceiver(ReceiverData data) {
        BroadcastReceiver.PendingResult result = data.resultData.build();
        try {
            // 依然是检测 Application 是否初始化,没有则初始化
            if (!isBound()) {
                bindApplication(data.component.getPackageName(), data.processName);
            }
            // 获取 Receiver 的 Context,这个context是一个ReceiverRestrictedContext实例,它有两个主要函数被禁掉:registerReceiver()和 bindService()。这两个函数在BroadcastReceiver.onReceive()不允许调用。每次Receiver处理一个广播,传递进来的context都是一个新的实例。
            Context context = mInitialApplication.getBaseContext();
            Context receiverContext = ContextImpl.getReceiverRestrictedContext.call(context);
            String className = data.component.getClassName();
            // 实例化目标 Receiver
            BroadcastReceiver receiver = (BroadcastReceiver) context.getClassLoader().loadClass(className).newInstance();
            mirror.android.content.BroadcastReceiver.setPendingResult.call(receiver, result);
            data.intent.setExtrasClassLoader(context.getClassLoader());
            // 手动调用 onCreate
            receiver.onReceive(receiverContext, data.intent);
            // 通知 Pending 结束
            if (mirror.android.content.BroadcastReceiver.getPendingResult.call(receiver) != null) {
                result.finish();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(
                    "Unable to start receiver " + data.component
                            + ": " + e.toString(), e);
        }
        // 这里需要远程通知 VAService 广播已送到
        VActivityManager.get().broadcastFinish(data.resultData);
    }

这里就是最关键的地方了,简单点概括就是 new 了真正的 Receiver 然后调用 onCreate 而已。Receiver 生命周期真的非常简单。

需要注意的是,broadCast 发送有个超时机制:

void broadcastFinish(PendingResultData res) {
        synchronized (mBroadcastRecords) {
            BroadcastRecord record = mBroadcastRecords.remove(res.mToken);
            if (record == null) {
                VLog.e(TAG, "Unable to find the BroadcastRecord by token: " + res.mToken);
            }
        }
        mTimeoutHandler.removeMessages(0, res.mToken);
        res.finish();
    }
private final class TimeoutHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            IBinder token = (IBinder) msg.obj;
            BroadcastRecord r = mBroadcastRecords.remove(token);
            if (r != null) {
                VLog.w(TAG, "Broadcast timeout, cancel to dispatch it.");
                r.pendingResult.finish();
            }
        }
    }

这里如果广播超时则会通知 PendingResult 结束,告诉发送方广播结束了。

发送广播的处理

其实上一部分已经讲了很多发送广播的处理了。
这里 Hook 了 broacastIntent 方法:

static class BroadcastIntent extends MethodProxy {

        @Override
        public String getMethodName() {
            return "broadcastIntent";
        }

        @Override
        public Object call(Object who, Method method, Object... args) throws Throwable {
            Intent intent = (Intent) args[1];
            String type = (String) args[2];
            intent.setDataAndType(intent.getData(), type);
            if (VirtualCore.get().getComponentDelegate() != null) {
                VirtualCore.get().getComponentDelegate().onSendBroadcast(intent);
            }
            Intent newIntent = handleIntent(intent);
            if (newIntent != null) {
                args[1] = newIntent;
            } else {
                return 0;
            }

            if (args[7] instanceof String || args[7] instanceof String[]) {
                // clear the permission
                args[7] = null;
            }
            return method.invoke(who, args);
        }


        private Intent handleIntent(final Intent intent) {
            final String action = intent.getAction();
            if ("android.intent.action.CREATE_SHORTCUT".equals(action)
                    || "com.android.launcher.action.INSTALL_SHORTCUT".equals(action)) {

                return VASettings.ENABLE_INNER_SHORTCUT ? handleInstallShortcutIntent(intent) : null;

            } else if ("com.android.launcher.action.UNINSTALL_SHORTCUT".equals(action)) {

                handleUninstallShortcutIntent(intent);

            } else {
                return ComponentUtils.redirectBroadcastIntent(intent, VUserHandle.myUserId());
            }
            return intent;
        }

        private Intent handleInstallShortcutIntent(Intent intent) {
            Intent shortcut = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
            if (shortcut != null) {
                ComponentName component = shortcut.resolveActivity(VirtualCore.getPM());
                if (component != null) {
                    String pkg = component.getPackageName();
                    Intent newShortcutIntent = new Intent();
                    newShortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);
                    newShortcutIntent.addCategory(Intent.CATEGORY_DEFAULT);
                    newShortcutIntent.putExtra("_VA_|_intent_", shortcut);
                    newShortcutIntent.putExtra("_VA_|_uri_", shortcut.toUri(0));
                    newShortcutIntent.putExtra("_VA_|_user_id_", VUserHandle.myUserId());
                    intent.removeExtra(Intent.EXTRA_SHORTCUT_INTENT);
                    intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, newShortcutIntent);

                    Intent.ShortcutIconResource icon = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
                    if (icon != null && !TextUtils.equals(icon.packageName, getHostPkg())) {
                        try {
                            Resources resources = VirtualCore.get().getResources(pkg);
                            int resId = resources.getIdentifier(icon.resourceName, "drawable", pkg);
                            if (resId > 0) {
                                //noinspection deprecation
                                Drawable iconDrawable = resources.getDrawable(resId);
                                Bitmap newIcon = BitmapUtils.drawableToBitmap(iconDrawable);
                                if (newIcon != null) {
                                    intent.removeExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
                                    intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, newIcon);
                                }
                            }
                        } catch (Throwable e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            return intent;
        }
  1. 这里拦截了创建快捷图标的 Intent,这是一个发给 Launcher 的隐式广播,VA 把这个请求拦截下来因为如果不拦截这个快捷方式就会指向外部的 App,并且如果外部 App 没有安装,此广播也不会发生作用。VA 把这个广播换成了自己的逻辑。
  2. 注意 ComponentUtils.redirectBroadcastIntent(), 类似 Activity 用代理 Intent 包裹真正的 Intent:
public static Intent redirectBroadcastIntent(Intent intent, int userId) {
        Intent newIntent = intent.cloneFilter();
        newIntent.setComponent(null);
        newIntent.setPackage(null);
        ComponentName component = intent.getComponent();
        String pkg = intent.getPackage();
        if (component != null) {
            newIntent.putExtra("_VA_|_user_id_", userId);
            // 这里显式意图被重定位成 _VA_PKGNAME_CLASSNAME 的格式,与前面注册的时候对应
            newIntent.setAction(String.format("_VA_%s_%s", component.getPackageName(), component.getClassName()));
            newIntent.putExtra("_VA_|_component_", component);
            newIntent.putExtra("_VA_|_intent_", new Intent(intent));
        } else if (pkg != null) {
            newIntent.putExtra("_VA_|_user_id_", userId);
            newIntent.putExtra("_VA_|_creator_", pkg);
            newIntent.putExtra("_VA_|_intent_", new Intent(intent));
            String protectedAction = SpecialComponentList.protectAction(intent.getAction());
            if (protectedAction != null) {
                newIntent.setAction(protectedAction);
            }
        } else {
            newIntent.putExtra("_VA_|_user_id_", userId);
            newIntent.putExtra("_VA_|_intent_", new Intent(intent));
            String protectedAction = SpecialComponentList.protectAction(intent.getAction());
            if (protectedAction != null) {
                newIntent.setAction(protectedAction);
            }
        }
        return newIntent;
    }

Ok, BroadcastReceiver 完毕,下一张分析最后一个组件 ContentProvider。

转载:https://blog.youkuaiyun.com/ganyao939543405/article/details/76229480

进击的Android注入术《五》 继续 在Android,几乎所有的IPC通讯都是 阅读详情

相关推荐

考试遇到 Assistant API 考点时,该掌握哪些要点?

【摘要】聚焦阿里云大模型 ACP 认证中智能体 Assistant API 考点,我结合自学认证成功经验,分享复习攻略。先解析 assistants、threads 等五个核心组件作用与关系,点明各组件支持及不支持的方法,最后给出计算 Python 代码调用大模型次数的思路,助力考生攻克考点、顺利拿证 。

davidkorhenn的博客 494

VirtualApp原理解析(2)--后台服务初始化流程

VirtualApp原理解析–后台服务初始化流程VirtualApp后台服务进程运行BinderProvider,其继承自ContentProvider。后台服务主要类:VAppMS,VAMS,VPMS等。主要功能:数据结构组建状态保存、模拟系统API的实现、预处理等。

leif 的博客 4208

DIKW模型实战:用Obsidian打造个人知识库的5个关键步骤

本文详细介绍了如何利用DIKW模型在Obsidian中构建高效个人知识库的5个关键步骤,从数据收集到智慧应用的全流程实践。通过标记系统、信息加工引擎和知识结晶方法,帮助知识工作者实现从原始数据到决策智慧的认知跃迁,提升个人知识管理效率。

gamma的博客 576

Android 双开沙箱 VirtualApp 源码分析(三)App 启动

上一章:Android 双开沙箱 VirtualApp 源码分析(二) 在这之前,我们还是要先了解一下 VA Client Framework 和 VAService 之间的通讯方式VAService 与通讯VAService首先,VAService 是指 VA 仿造 Android 原生 framework 层 Service 实现的一套副本,举例有 VActivityManagerServic

ganyao939543405的博客 1万+

RK3568笔记五十四:SORT实时目标追踪

YOLOv5具备目标检测的功能,把视频分解成多幅图像并逐帧执行时,如果视频帧中有多个目标,如何知道一帧中的目标和上一帧是同一个对象就是目标跟踪的工作。DeepSort是实现目标跟踪的算法,从sort(simple online and realtime tracking)演变而来,使用卡尔曼滤波器预测所检测对象的运动轨迹,匈牙利算法将它们与新的检测目标相匹配。结合YOLOv5和DeepSORT可以在实时场景中实现高效的目标检测和跟踪。

788

Qt入门教程【高级控件篇】QModelIndex索引

QModelIndex索引

小鱼酱的博客 4466

VirtualApp沙盒基本原理

转自:http://rk700.github.io/2017/03/15/virtualapp-basic/ VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。通过阅读源码及动态调试,基本了解了其运行原理,在此记录。 本质 Android应用隔离是基于Linux系统的多用户机制实现的,即每个应用在安装

云守护的专栏 2万+

基于锁相环的高精度频率分频器simulink建模与仿真

在现代电子系统中,高精度频率分频器扮演着至关重要的角色。它能够提供精确的频率参考,广泛应用于通信、仪器仪表、航空航天等领域。基于锁相环的高精度频率分频器具有结构简单、性能稳定等优点,成为了研究的热点。

平时工作较为繁忙,私信消息一般晚上回复,谢谢大家~~ 776

VirtualApp 中的进程

一. 什么是进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。 – 来自百度百科 二. VA进程VA在运行时,存在四种类型的进程:

ximsfei的专栏 2811

基于LSTM算法实现交通流量预测(Pytorch版)

本研究采用PyTorch框架,设计并实现了一种基于LSTM(Long Short-Term Memory)网络的交通流量预测模型。针对时序性显著的交通流量数据,模型首先将多节点、多通道的时空信息整合为一维特征输入至多层LSTM单元,利用其捕捉长期依赖及短期波动的能力进行深度学习。全连接层随后调整时间步长以适应预测周期需求。模型结构灵活,能适应不同节点数、输入输出长度及通道配置,实现在给定历史流量数据基础上对未来交通流量的精准预测,为交通管理与规划提供数据驱动支持。

打酱油的葫芦娃 4637

如何实现一个用STM32F103C8T6作为主控芯片,加上AD8232、MAX30102 、MPU6050模块的用于运动中监测心率变异性??

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1212

VirtualApp 进程分配与管理

一. 前景 在Android中每个App在启动前必须先创建一个进程,该进程是由Zygote fork出来的,进程具有独立的资源空间,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml

ximsfei的专栏 3983

SAP-ABAP:新手入门篇——从0到1写出你的第一个ABAP Hello World程序并完成调试运行

本文是ABAP开发入门指南,详细介绍如何创建并运行第一个"Hello World"程序。主要内容包括:通过SE38事务码进入ABAP编辑器;创建以Z/Y开头的程序并保存为本地对象;编写简单的WRITE输出语句;完成语法检查、激活和执行流程;以及初步体验调试功能。文章特别强调ABAP开发的规范要求,如程序命名规则、激活与保存的区别等,并提供了详细的步骤说明和常见问题提示,帮助零基础开发者快速上手ABAP编程。

baidu_35680696的博客 479

VirtualApp原理解析(1)--初始化及注入流程

VirtualApp原理解析 初始化及注入流程 VirtualApp是一款可以达到应用双开效果的开源项目。VirtualApp整体流程,包含启动双开应用Activity的过程。

leif 的博客 1万+

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

virtualapp-IPCBus跨进程通讯(转载)

IPCBus跨进程通讯

hgy413的专栏 1083

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider

上一章:Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiverProvider 注册回顾前面,Activity 启动的时候会检查 Application 是否初始化,会调用 bindApplication,里面执行了安装 Provider 的方法:private void installContentProviders(Context app, List<P

ganyao939543405的博客 7051

BroadCast Receiver(广播)详解

BroadCastReceiver是四大组件之一,是一个全局的监听器BroadCastReceiver有两大功能,广播发送者,广播接收者BroadCastReceiver分为静态广播和动态动态广播。

suiyue010211的博客 8405

Android沙箱

Android沙箱是一种隔离机制,用于在Android系统中保护应用程序的运行环境和数据安全。每个Android应用程序都运行在自己的沙箱中,类似于一个隔离的空间,防止它与其他应用程序或系统资源直接交互。这种设计主要依赖于Linux内核的用户隔离和权限管理功能,以确保应用之间的数据和功能互不干扰。

Bileton的博客 2378

【Android 插件化】VirtualApp 编译运行 ( VirtualApp 简介 | 配置 VirtualApp 编译环境 | 编译运行 VirtualApp 官方示例 )

一、VirtualApp 简介、 二、配置 VirtualApp 编译环境、 1、Android Studio 3.0、 2、NDK 版本 ( Android NDK r10e )、 3、配置远程仓库、 三、编译运行 VirtualApp 官方示例、 四、编译好的 VirtualApp 官方源码、 总结 ( 编译配置如下 )、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 7338

【Android Framework系列】第17章 Android Q沙箱模式(Scoped Storage)

App TargetSDK > 28 即 Android10(Q)及以上项目,Google限制了存储沙箱模式,在Android10(Q)以上的设备建议使用私有目录data/data,无法再直接访问外部SD卡存储目录,如需要使用外部SD卡存储目录则需要通过SAF、MediaStore接口访问,并且只能访问特定的外部SD卡存储目录,如Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones

Yvan 3077

Android 双开沙箱 VirtualApp 源码分析(一)

最近发现了一个非常好的开源项目,基本实现了一个 Android 上的沙箱环境,不过应用场景最多的还是应用双开。 VA github: https://github.com/asLody/VirtualApp VA 的源码注释: https://github.com/ganyao114/VA_Doc 第一章主要是分析一下项目的整体结构。包结构android.content主要是 PackageP

ganyao939543405的博客 4万+

Android的SandBox(沙箱)

在研究Android系统时,有时会遇到Sandbox(沙箱)这个概念。沙箱概念本身并不是太新奇,但是一直不是很清楚Android是如何实现它所称的“沙箱”的。网上不少人声称应用使用了虚拟机就是应用了沙箱,一直对这种说法表示怀疑。 最近发现Android的网站上更新了一些文档,其中包括对Sandbox的解释,这才明白Android中Sandbox的含义。 Android的”沙箱“

u013234805的专栏 9816

Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service

上一章:Android 双开沙箱 VirtualApp 源码分析(三)App 启动原生 Service 创建过程首先有必要了解一下原生 framework 对 Service 的创建,因为在 VA 中启动 Service 和 Activity 有很大的区别。首先入口 ContextWrapper.startService():@Override public ComponentName st

ganyao939543405的博客 7800

(4.1.55)Android 沙箱机制

(4.1.55)Android 沙箱机制 沙箱(sandbox)是为执行中的程序提供隔离环境的一种安全机制。它通过严格控制执行的程序所访问的资源,以确保系统的安全。 文章目录一、Android系统架构二、Android安全机制三、Android沿用Linux权限模型四、Android沙箱模型4.1 应用程序在独立的进程4.2 应用程序在同一个进程(共享UID)参考文献 一、Android系统架...

JAVA成长之路 1万+

Android系统广播(3)–客户端广播处理流程

Android系统广播(3)–客户端广播处理流程

leif 的博客 929
上一篇: Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service
下一篇: Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider
CoderAldrich
博客等级 码龄12年 431粉丝 24原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值