BackupManagerService分析 基于Android 8.0
2018-02-07 21:11
471 查看
最近遇到BackupManagerService备份恢复后桌面小部件重复添加达到800个之多的问题,严重影响系统体验,所以抽出半天时间进行分析.
废话不多说先看下BackupManagerService dump出来的信息从中窥探它的大概原理以及结构
首先可以肯定BackupManagerService肯定是一个调度框架
其次它调度什么呢,肯定是Transport,这里可以看到有白名单,和可用的 transports,还有Transport执行任务的历史数据.
另外后面的几项信息
Pending init是什么? 从字意思看应该是待执行的初始化
Participants是参加者的含义到底是用来做什么的?
Ancestral字面意思是先祖,到底是干什么用的
另外三项好像还比较理解
Ever backed up:
Pending key/value backup 延时的keyvalue 备份
Full backup queue 完全备份队列
好了带着这些疑问我们进行分析.
值得注意的是Trampoline是真正实现IBackupManager.Stub接口的类,向外部提供服务. 另外TransportManager可定是用于管理transport的,这里进行初始化.我们看下初始化逻辑
只是简单的赋值,这里面连个关键的数据whitelist和defaultTransport已经传进来了.
我们再看一下TransportManager.registerAllTransports()函数,注意参数都null
代码也很简单, 查询Action是android.backup.TRANSPORT_HOST的service,如果查询到的组件在白名单且是预装在priv-app下的应用,则认为是可以新人的组件,绑定它的service,绑定的过程很简单我们看下绑定完成执行的onServiceConnected函数,代码在frameworks/base/services/backup/java/com/android/server/backup/TransportManager.java文件中.
添加到mBoundTransports集合里面,代表已经绑定成功,另外绑定成功后又添加到mValidTransports中代表组件可用.
另外执行了mTransportBoundListener的回调,函数的代码在BackupManagerService类中.是一个内部成员类
这里检查/data/backup/${transportDirName}/need_init文件是否存在,如果存在则建立一个mPendingInits任务,一分钟后唤醒执行mRunInitIntent描述的一个广播任务. 可见mRunInitIntent所描述的任务肯定是用于处理mPendingInits的. 另外${transportDirName}为绑定的transport自己定义的路径. 第一次绑定肯定不会有这个文件的,所以我们先不去分析初始化任务.
BackupManagerService如何启动还没有分析,这里就不去翻SystemServer代码了,是通过
BackupManagerService.Lifecycle启动的,Lifecycle实现了SystemService
启动直到onUnlockUser后就能读取到非dirctBoot的service了sInstance.initialize(userId);函数就是用于创建BackupManagerService,做初始化操作. 之后要根据backup_enabled值去写/data/backup/backup_enabled文件,别问我为啥还要做这一步周转,我目前也不知道,我们慢慢分析.
接下来,整个初始化的关键,让整个系统跑起来的关键就在 sInstance.setBackupEnabled(readBackupEnableState(userId));这一步.会调用到BackupManagerService的setBackupEnabled函数.
在开启状态的时候会执行 KeyValueBackupJob.schedule(mContext);
scheduleNextFullBackupJob(0); 函数,这是整个系统跑起来的关键,为了防止读代码时候太跳跃我们先看下变成不可用的情况,给每个allTransports添加了一个mPendingInit任务,并要求马上执行.
这下就可以看两个schedule函数了.
先看KeyValueBackupJob,代码在frameworks/base/services/backup/java/com/android/server/backup/KeyValueBackupJob.java中
这里添加了一个JobScheduler任务,延时4个小时到一天,真正执行任务的service是sKeyValueJobService(其实就是KeyValueBackupJob这个service),并且需要插电. 可见备份事件还是不太紧急的
这里只是调用了BackupManagerService.backupNow()函数.
函数发了一个mRunBackupIntent描述的pendingIntent任务,然后取消job,我们来看看如何处理这个任务
在RunBackupReceiver内部实例中处理该广播RUN_BACKUP_ACTION
这里有遇到了mPendingInits我们先不管它,直接分析else部分,如果备份可用并且当前没有正在运行的备份任务,就发送一个MSG_RUN_BACKUP给后台线程的handler处理
处理这个消息的部分在BackupManagerService的内部实例BackupHandler的handleMessage中,这部分代码也比较长.
其实逻辑很简单,就是收集mPendingBackups放到queue集合里面.并创建PerformBackupTask作为一个备份任务去,并创建MSG_BACKUP_RESTORE_STEP去执行,这里只是因为分步去做才又进行了一次倒手,其实处理该任务的handler还是这个线程同一个handler. 我们先不管mPendingBackups哪里来的,这里告诉读者,是允许备份的包护数据变化后会通知BackupManagerService并添加PendingBackup任务.我们先去分析如何执行备份任务,回来在分析任务的添加过程.
这里task实际类型为PerformBackupTask,我们只需要看下它的execute()函数就能知道其中逻辑.
这里要执行的是void beginBackup()函数
废话不多说先看下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.LocalTransportService com.google.android.gms/.backup.component.D2dTransportService com.google.android.gms/.backup.BackupTransportService Available transports: android/com.android.internal.backup.LocalTransport destination: Backing up to debug-only private cache intent: null com.google.android.gms/.backup.migrate.service.D2dTransport destination: Moving data to new device intent: null * com.google.android.gms/.backup.BackupTransportService destination: hackerdevdas@gmail.com intent: Intent { cmp=com.google.android.gms/.backup.SetBackupAccountActivity } @pm@ - 3086 state bytes com.google.android.apps.maps - 124 state bytes android - 477 state bytes com.google.android.apps.messaging - 124 state bytes com.android.cellbroadcastreceiver - 116 state bytes com.google.android.googlequicksearchbox - 116 state bytes com.google.android.videos - 72 state bytes com.google.android.googlequicksearchbox_widget - 95 state bytes com.google.android.contacts - 124 state bytes com.android.providers.settings - 84 state bytes Pending init: 0 Ancestral: 0 Current: 3fea8d5038fd8e55 Participants: uid: 1000 com.android.providers.settings android uid: 10000 com.android.providers.blockednumber com.android.calllogbackup com.android.providers.userdictionary uid: 10009 com.android.cellbroadcastreceiver uid: 10012 Ancestral packages: none Ever backed up: 28 com.google.android.apps.maps android com.google.android.apps.messaging com.android.cellbroadcastreceiver com.google.android.googlequicksearchbox com.google.android.videos com.google.android.contacts com.android.providers.settings com.google.android.marvin.talkback com.google.android.play.games com.android.calllogbackup com.google.android.apps.inputmethod.hindi Pending key/value backup: 31 BackupRequest{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:68 0 : com.studio71.sunday_suspense 0 : com.qapp.secprotect 0 : se.dirac.acs 0 : com.android.bips 0 : com.android.carrierdefaultapp 0 : com.qti.vzw.ims.internal.tests 0 : com.qualcomm.timeservice 1511525714741 : com.google.android.ext.services 1511525862187 : com.android.launcher3 1511526633498 : com.google.android.backuptransport 1511530346032 : com.android.smspush 1511606005489 : com.lenovo.anyshare.gps 1511606022751 : com.graymatrix.did 1511606025293 : com.jio.media.ondemand 1511606027829 : 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() { @Override public 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 minute long delay = 1000 * 60; // one minute, in milliseconds mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delay, mRunInitIntent); } } return true; } catch (Exception e) { // the transport threw when asked its file naming prefs; declare it invalid Slog.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); } @Override public void onStart() { publishBinderService(Context.BACKUP_SERVICE, sInstance); } @Override public void onUnlockUser(int userId) { if (userId == UserHandle.USER_SYSTEM) { sInstance.initialize(userId); // Migrate legacy setting if (!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 passes KeyValueBackupJob.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 init for (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 it KeyValueBackupJob.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()函数
相关文章推荐
- Android中ActivityManagerService与应用程序(客户端)通信模型分析
- Android Binder机制 - defaultServiceManager()源码分析
- Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解
- Android PackageManagerService详细分析
- Android中ActivityManagerService与应用程序(客户端)通信模型分析
- Android的软件包管理服务PackageManagerService源码分析
- [置顶] Android开发之serviceManager分析
- Android Framework 之PackageManagerService详细分析
- Android PackageManagerService详细分析
- Android ActivityManagerService(AMS)的启动分析
- Android SensorManager,SystemSensorManager,SensorService调用过程分析
- android 8.0 MountService -->StorageManagerService
- 基于Andoird 4.2.2的Account Manager源代码分析学习:AccountManagerService系统服务的添加
- Android ActivityManagerService(AMS)的启动分析
- Android PackageManagerService启动流程分析
- Android 4.4 KitKat NotificationManagerService使用详解与原理分析(二)__原理分析
- Android PackageManagerService流程详细分析(三)之PackageHandler
- [Android 系统源代码研究] ActivityManagerService情景分析
- android PowerManagerService分析
- Android N NotificationManagerService源码分析