BackupManagerService分析 基于Android 8.0
最近遇到BackupManagerService备份恢复后桌面小部件重复添加达到800个之多的问题,严重影响系统体验,所以抽出半天时间进行分析.
废话不多说先看下BackupManagerService dump出来的信息从中窥探它的大概原理以及结构
DUMP OF SERVICE backup:
Backup Manager is enabled / provisioned / not pending init
Auto-restore is enabled
Last backup pass started: 0 (now = 1516085568739)next scheduled: 1516056771175
Transport whitelist:android/com.android.internal.backup.LocalTransportServicecom.google.android.gms/.backup.component.D2dTransportServicecom.google.android.gms/.backup.BackupTransportService
Available transports:android/com.android.internal.backup.LocalTransportdestination: Backing up to debug-only private cacheintent: nullcom.google.android.gms/.backup.migrate.service.D2dTransportdestination: Moving data to new deviceintent: null* com.google.android.gms/.backup.BackupTransportServicedestination: hackerdevdas@gmail.comintent: Intent { cmp=com.google.android.gms/.backup.SetBackupAccountActivity }@pm@ - 3086 state bytescom.google.android.apps.maps - 124 state bytesandroid - 477 state bytescom.google.android.apps.messaging - 124 state bytescom.android.cellbroadcastreceiver - 116 state bytescom.google.android.googlequicksearchbox - 116 state bytescom.google.android.videos - 72 state bytescom.google.android.googlequicksearchbox_widget - 95 state bytescom.google.android.contacts - 124 state bytescom.android.providers.settings - 84 state bytes
Pending init: 0
Ancestral: 0
Current: 3fea8d5038fd8e55
Participants:uid: 1000com.android.providers.settingsandroiduid: 10000com.android.providers.blockednumbercom.android.calllogbackupcom.android.providers.userdictionaryuid: 10009com.android.cellbroadcastreceiveruid: 10012
Ancestral packages: none
Ever backed up: 28com.google.android.apps.mapsandroidcom.google.android.apps.messagingcom.android.cellbroadcastreceivercom.google.android.googlequicksearchboxcom.google.android.videoscom.google.android.contactscom.android.providers.settingscom.google.android.marvin.talkbackcom.google.android.play.gamescom.android.calllogbackupcom.google.android.apps.inputmethod.hindi
Pending key/value backup: 31BackupRequest{pkg=com.google.android.apps.maps}BackupRequest{pkg=android}BackupRequest{pkg=com.google.android.apps.messaging}BackupRequest{pkg=com.android.cellbroadcastreceiver}BackupRequest{pkg=com.google.android.googlequicksearchbox}BackupRequest{pkg=com.google.android.videos}BackupRequest{pkg=com.google.android.contacts}BackupRequest{pkg=com.android.providers.settings}
Full backup queue:680 : com.studio71.sunday_suspense0 : com.qapp.secprotect0 : se.dirac.acs0 : com.android.bips0 : com.android.carrierdefaultapp0 : com.qti.vzw.ims.internal.tests0 : com.qualcomm.timeservice1511525714741 : com.google.android.ext.services1511525862187 : com.android.launcher31511526633498 : com.google.android.backuptransport1511530346032 : com.android.smspush1511606005489 : com.lenovo.anyshare.gps1511606022751 : com.graymatrix.did1511606025293 : com.jio.media.ondemand1511606027829 : com.android.cts.priv.ctsshim
--------- 0.013s was the duration of dumpsys backup
首先可以肯定BackupManagerService肯定是一个调度框架
其次它调度什么呢,肯定是Transport,这里可以看到有白名单,和可用的 transports,还有Transport执行任务的历史数据.
另外后面的几项信息
Pending init是什么? 从字意思看应该是待执行的初始化
Participants是参加者的含义到底是用来做什么的?
Ancestral字面意思是先祖,到底是干什么用的
另外三项好像还比较理解
Ever backed up:
Pending key/value backup 延时的keyvalue 备份
Full backup queue 完全备份队列
好了带着这些疑问我们进行分析.
public BackupManagerService(Context context, Trampoline parent) {......mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());....Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();String transport = Settings.Secure.getString(context.getContentResolver(),Settings.Secure.BACKUP_TRANSPORT);......mTransportManager = new TransportManager(context, transportWhitelist, currentTransport,mTransportBoundListener, mHandlerThread.getLooper());mTransportManager.registerAllTransports();......}
值得注意的是Trampoline是真正实现IBackupManager.Stub接口的类,向外部提供服务. 另外TransportManager可定是用于管理transport的,这里进行初始化.我们看下初始化逻辑
TransportManager(Context context, Set<ComponentName> whitelist, String defaultTransport,TransportBoundListener listener, Looper looper) {mContext = context;mPackageManager = context.getPackageManager();mTransportWhitelist = (whitelist != null) ? whitelist : new ArraySet<>();mCurrentTransportName = defaultTransport;mTransportBoundListener = listener;mHandler = new RebindOnTimeoutHandler(looper);}
只是简单的赋值,这里面连个关键的数据whitelist和defaultTransport已经传进来了.
我们再看一下TransportManager.registerAllTransports()函数,注意参数都null
private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
private static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";private void bindToAllInternal(String packageName, String[] components) {PackageInfo pkgInfo = null;
...Intent intent = new Intent(mTransportServiceIntent);if (packageName != null) {intent.setPackage(packageName);}List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(intent, 0, UserHandle.USER_SYSTEM);if (hosts != null) {for (ResolveInfo host : hosts) {final ComponentName infoComponentName = host.serviceInfo.getComponentName();boolean shouldBind = false;if (components != null && packageName != null) {for (String component : components) {ComponentName cn = new ComponentName(pkgInfo.packageName, component);if (infoComponentName.equals(cn)) {shouldBind = true;break;}}} else {shouldBind = true;}if (shouldBind && isTransportTrusted(infoComponentName)) {tryBindTransport(infoComponentName);}}}}private void tryBindTransport(ComponentName transportComponentName) {Slog.d(TAG, "Binding to transport: " + transportComponentName.flattenToShortString());// TODO: b/22388012 (Multi user backup and restore)TransportConnection connection = new TransportConnection(transportComponentName);if (bindToTransport(transportComponentName, connection)) {synchronized (mTransportLock) {mValidTransports.put(transportComponentName, connection);}} else {Slog.w(TAG, "Couldn't bind to transport " + transportComponentName);}}
代码也很简单, 查询Action是android.backup.TRANSPORT_HOST的service,如果查询到的组件在白名单且是预装在priv-app下的应用,则认为是可以新人的组件,绑定它的service,绑定的过程很简单我们看下绑定完成执行的onServiceConnected函数,代码在frameworks/base/services/backup/java/com/android/server/backup/TransportManager.java文件中.
success = mTransportBoundListener.onTransportBound(mBinder);
mBoundTransports.put(mTransportName, component);
添加到mBoundTransports集合里面,代表已经绑定成功,另外绑定成功后又添加到mValidTransports中代表组件可用.
另外执行了mTransportBoundListener的回调,函数的代码在BackupManagerService类中.是一个内部成员类
private TransportManager.TransportBoundListener mTransportBoundListener =new TransportManager.TransportBoundListener() {@Overridepublic boolean onTransportBound(IBackupTransport transport) {// If the init sentinel file exists, we need to be sure to perform the init// as soon as practical. We also create the state directory at registration// time to ensure it's present from the outset.String name = null;try {name = transport.name();String transportDirName = transport.transportDirName();File stateDir = new File(mBaseStateDir, transportDirName);stateDir.mkdirs();File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);if (initSentinel.exists()) {synchronized (mQueueLock) {mPendingInits.add(name);// TODO: pick a better starting time than now + 1 minutelong delay = 1000 * 60; // one minute, in millisecondsmAlarmManager.set(AlarmManager.RTC_WAKEUP,System.currentTimeMillis() + delay, mRunInitIntent);}}return true;} catch (Exception e) {// the transport threw when asked its file naming prefs; declare it invalidSlog.w(TAG, "Failed to regiser transport: " + name);return false;}}};
这里检查/data/backup/${transportDirName}/need_init文件是否存在,如果存在则建立一个mPendingInits任务,一分钟后唤醒执行mRunInitIntent描述的一个广播任务. 可见mRunInitIntent所描述的任务肯定是用于处理mPendingInits的. 另外${transportDirName}为绑定的transport自己定义的路径. 第一次绑定肯定不会有这个文件的,所以我们先不去分析初始化任务.
BackupManagerService如何启动还没有分析,这里就不去翻SystemServer代码了,是通过
BackupManagerService.Lifecycle启动的,Lifecycle实现了SystemService
public static final class Lifecycle extends SystemService {public Lifecycle(Context context) {super(context);sInstance = new Trampoline(context);}@Overridepublic void onStart() {publishBinderService(Context.BACKUP_SERVICE, sInstance);}@Overridepublic void onUnlockUser(int userId) {if (userId == UserHandle.USER_SYSTEM) {sInstance.initialize(userId);// Migrate legacy settingif (!backupSettingMigrated(userId)) {if (DEBUG) {Slog.i(TAG, "Backup enable apparently not migrated");}final ContentResolver r = sInstance.mContext.getContentResolver();final int enableState = Settings.Secure.getIntForUser(r,Settings.Secure.BACKUP_ENABLED, -1, userId);if (enableState >= 0) {if (DEBUG) {Slog.i(TAG, "Migrating enable state " + (enableState != 0));}writeBackupEnableState(enableState != 0, userId);Settings.Secure.putStringForUser(r,Settings.Secure.BACKUP_ENABLED, null, userId);} else {if (DEBUG) {Slog.i(TAG, "Backup not yet configured; retaining null enable state");}}}try {sInstance.setBackupEnabled(readBackupEnableState(userId));} catch (RemoteException e) {// can't happen; it's a local object}}}}
启动直到onUnlockUser后就能读取到非dirctBoot的service了sInstance.initialize(userId);函数就是用于创建BackupManagerService,做初始化操作. 之后要根据backup_enabled值去写/data/backup/backup_enabled文件,别问我为啥还要做这一步周转,我目前也不知道,我们慢慢分析.
接下来,整个初始化的关键,让整个系统跑起来的关键就在 sInstance.setBackupEnabled(readBackupEnableState(userId));这一步.会调用到BackupManagerService的setBackupEnabled函数.
public void setBackupEnabled(boolean enable) {......boolean wasEnabled = mEnabled;synchronized (this) {writeBackupEnableState(enable, UserHandle.USER_SYSTEM);mEnabled = enable;}synchronized (mQueueLock) {if (enable && !wasEnabled && mProvisioned) {// if we've just been enabled, start scheduling backup passesKeyValueBackupJob.schedule(mContext);scheduleNextFullBackupJob(0);} else if (!enable) {KeyValueBackupJob.cancel(mContext);if (wasEnabled && mProvisioned) {// NOTE: we currently flush every registered transport, not just// the currently-active one.String[] allTransports = mTransportManager.getBoundTransportNames();// build the set of transports for which we are posting an initfor (String transport : allTransports) {recordInitPendingLocked(true, transport);}mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),mRunInitIntent);}}}} ...}
在开启状态的时候会执行 KeyValueBackupJob.schedule(mContext);
scheduleNextFullBackupJob(0); 函数,这是整个系统跑起来的关键,为了防止读代码时候太跳跃我们先看下变成不可用的情况,给每个allTransports添加了一个mPendingInit任务,并要求马上执行.
这下就可以看两个schedule函数了.
先看KeyValueBackupJob,代码在frameworks/base/services/backup/java/com/android/server/backup/KeyValueBackupJob.java中
private static ComponentName sKeyValueJobService =new ComponentName("android", KeyValueBackupJob.class.getName());public static void schedule(Context ctx, long delay) {synchronized (KeyValueBackupJob.class) {if (!sScheduled) {if (delay <= 0) {delay = BATCH_INTERVAL + new Random().nextInt(FUZZ_MILLIS);}if (BackupManagerService.DEBUG_SCHEDULING) {Slog.v(TAG, "Scheduling k/v pass in "+ (delay / 1000 / 60) + " minutes");}JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService).setMinimumLatency(delay).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).setRequiresCharging(true).setOverrideDeadline(MAX_DEFERRAL);js.schedule(builder.build());sNextScheduled = System.currentTimeMillis() + delay;sScheduled = true;}}}
这里添加了一个JobScheduler任务,延时4个小时到一天,真正执行任务的service是sKeyValueJobService(其实就是KeyValueBackupJob这个service),并且需要插电. 可见备份事件还是不太紧急的
public boolean onStartJob(JobParameters params) {synchronized (KeyValueBackupJob.class) {sNextScheduled = 0;sScheduled = false;}// Time to run a key/value backup!Trampoline service = BackupManagerService.getInstance();try {service.backupNow();} catch (RemoteException e) {}// This was just a trigger; ongoing wakelock management is done by the// rest of the backup system.return false;}
这里只是调用了BackupManagerService.backupNow()函数.
Intent backupIntent = new Intent(RUN_BACKUP_ACTION);backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);public void backupNow() {......final PowerSaveState result =mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);if (result.batterySaverEnabled) {if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");KeyValueBackupJob.schedule(mContext); // try again in several hours} else {if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");synchronized (mQueueLock) {mRunBackupIntent.send();......// ...and cancel any pending scheduled job, because we've just superseded itKeyValueBackupJob.cancel(mContext);}}}
函数发了一个mRunBackupIntent描述的pendingIntent任务,然后取消job,我们来看看如何处理这个任务
在RunBackupReceiver内部实例中处理该广播RUN_BACKUP_ACTION
private class RunBackupReceiver extends BroadcastReceiver {public void onReceive(Context context, Intent intent) {if (RUN_BACKUP_ACTION.equals(intent.getAction())) {synchronized (mQueueLock) {if (mPendingInits.size() > 0) {.....} else {if (mEnabled && mProvisioned) {if (!mBackupRunning) {mBackupRunning = true;mWakelock.acquire();Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);mBackupHandler.sendMessage(msg);} else {Slog.i(TAG, "Backup time but one already running");}} else {Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);}}}}}}
这里有遇到了mPendingInits我们先不管它,直接分析else部分,如果备份可用并且当前没有正在运行的备份任务,就发送一个MSG_RUN_BACKUP给后台线程的handler处理
处理这个消息的部分在BackupManagerService的内部实例BackupHandler的handleMessage中,这部分代码也比较长.
ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();File oldJournal = mJournal;synchronized (mQueueLock) {if (mPendingBackups.size() > 0) {for (BackupRequest b: mPendingBackups.values()) {queue.add(b);}mPendingBackups.clear();// Start a new backup-queue journal file too......}} if (queue.size() > 0) {// Spin up a backup state sequence and set it running......PerformBackupTask pbt = new PerformBackupTask(transport, dirName, queue,oldJournal, null, null, Collections.<String>emptyList(), false,false /* nonIncremental */);Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);sendMessage(pbtMessage);
其实逻辑很简单,就是收集mPendingBackups放到queue集合里面.并创建PerformBackupTask作为一个备份任务去,并创建MSG_BACKUP_RESTORE_STEP去执行,这里只是因为分步去做才又进行了一次倒手,其实处理该任务的handler还是这个线程同一个handler. 我们先不管mPendingBackups哪里来的,这里告诉读者,是允许备份的包护数据变化后会通知BackupManagerService并添加PendingBackup任务.我们先去分析如何执行备份任务,回来在分析任务的添加过程.
case MSG_BACKUP_RESTORE_STEP:{try {BackupRestoreTask task = (BackupRestoreTask) msg.obj;if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing");task.execute();} catch (ClassCastException e) {Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);}break;}
这里task实际类型为PerformBackupTask,我们只需要看下它的execute()函数就能知道其中逻辑.
public void execute() {synchronized (mCancelLock) {switch (mCurrentState) {case INITIAL:beginBackup();break;case RUNNING_QUEUE:invokeNextAgent();break;case FINAL:if (!mFinished) finalizeBackup();else {Slog.e(TAG, "Duplicate finish");}mFinished = true;break;}}}
这里要执行的是void beginBackup()函数
BackupManagerService分析 基于Android 8.0相关推荐
- 华为内测基于Android 10.0的EMUI 10系统;2019年Q1真无线耳机市场份额,苹果占半壁江山……...
关注并标星星CSDN云计算 极客头条:速递.最新.绝对有料.这里有企业新动.这里有业界要闻,打起十二分精神,紧跟fashion你可以的! 每周三次,打卡即read 更快.更全了解泛云圈精彩news g ...
- 基于 Android 6.0 的 小米 MIUI 8 已开源
MIUI是小米科技推出的一款基于CyanogenMod而深度定制的Android移动操作系统,从Android 4.0开始,已经完全脱离CyanogenMod而改为基于原厂ROM的定制方式,中文名称为 ...
- 华为EMUI 10系统内测截图曝光:基于Android 10.0
日前有消息称,华为正在内测基于Android 10.0系统的EMUI 10系统,该系统不仅在内核方面得以升级,更会内置全套谷歌GMS套件. 从目前流出的截图来看,内测机型仅限今年上半年推出的旗舰产品华 ...
- 基于Android 9.0 电池温度异常提醒
基于Android 9.0 电池温度异常提醒 参考文章: (1)基于Android 9.0 电池温度异常提醒 (2)https://www.cnblogs.com/wangjie1990/p/1132 ...
- miui 10 android 9,MIUI10开发版8.12.13发布 基于Android 9.0 小米Max3已更新
截止目前,MIUI10开发版8.12.13已发布.其实,要按照MIUI官方推送的时间来计算,我手里的小米Max3早在前天就应该收到更新提醒.不过,稍微有些遗憾地是,由于小米Max3在抽屉里搁置太久,以 ...
- flyme最新7基于android,终于来了,魅族开始基于Android 7.0版本的Flyme内测
拼 命 加 载 中 ... 在大部分厂商已经跟进基于Android 7.0的版本时,作为国内最著名的定制ROM之一的Flyme还停留在基于Android 6.0的版本,在系统更新速度上落后了很多.不过 ...
- 海信电视power android,海信85U9E评测:基于Android 9.0深度优化的电视系统
关注手机行业的朋友应该注意到了,从今年开始,不但已经有厂商将手机拍照提升到 1 亿像素级别,还纷纷发力手机 8K 视频拍摄,虽然这多少有点炫技成分,但不得不说随着人们对视频内容的需求不断增加,我们生活 ...
- 氢os android 7.0,氢OS 3.0正式推送!基于Android 7.0,简洁纯净
原标题:氢OS 3.0正式推送!基于Android 7.0,简洁纯净 一加手机自推出伊始就坚持"不将就"的品牌理念,用顶级硬件和较低的价格以及众多定制ROM俘获了众多"加 ...
- 联想Android+7,联想ZUK发布ZUI 2.5:基于Android 7.0 优化很强大
原标题:联想ZUK发布ZUI 2.5:基于Android 7.0 优化很强大 [PConline 资讯]相信很多喜欢联想的用户对于ZUK这个品牌很熟悉吧,做为联想旗下的品牌,ZUK一直也很给力. 而今 ...
最新文章
- 2018-3-31 文章(ELM-Chinese-Brief)原文
- JavaScript实现自动评论哔哩哔哩视频
- suffix tree
- centos查看网关地址
- 如何使用JavaScript实现纯前端读取和导出excel文件(转)
- python gis库_使用开放的python库自动化GIS和遥感工作流
- 【汇编语言】清华大学学堂在线《汇编语言程序设计》课程学习笔记
- 神经网络基本原理简明教程-0-Python-Numpy库的点滴
- 【Android开发—智能家居系列】(三):手机连接WIFI模块
- 阿里云服务器下安装LAMP环境(CentOS Linux 6.3)(1)
- 利用Enterprise Library中的DAAB构造的数据库访问架构
- 从Gradient Descent 到 Stochastic Gradient Descent(SGD)
- ai人工智能电话机器人应用市场分析
- MathCAD14破解版
- 《Netlogo多主体建模入门》学习笔记(2)
- 英特尔核显无法为此计算机,win10intel显卡驱动装不上怎么办_Win10系统无法安装intel显卡驱动如何解决...
- 测量脉冲宽度仿真proteus
- 轻松搞定iOS自动化环境搭建
- HSSF、XSSF、SXSSF、对Excel的区别,以及对应不同后缀名Excel(xls,xlsx)的使用
- AngularJS车牌号正则表达式验证
热门文章
- 名著中的经典语录(转自网络)
- POJ 3290 WFF 'N PROOF 英文少
- python 删除pdf页面_删除PDF其中几页的方法
- 看设计师大牛如何将用户体验与建站融会贯通
- pdf翻译,两款pdf文件翻译软件,支持linux/ubuntu,window,mac下使用
- mysql rds数据库优势_云数据库 RDS MySQL究竟有何优势呢?
- 基于ResNetRS的宝可梦图像识别
- bag of tricks for image classification with convolutional neural networks
- 破解网易云js加密,爬虫获取网易云评论
- Educational Codeforces Round 92 (Rated for Div. 2)题解(A-C)