1.1 概述
SyncManager是Android提供的一个同步框架,该框架实施了许多最佳做法,它允许Android应用使用Google应用中实现高效同步的一个基本框架。该功能是 ContentService 中负责数据同步管理的 SyncManager ,ContentService 承担了数据更新通知机制的工作,同步服务管理的工作则委托给 SyncManager 来完成。另外,可以通过SyncAdapter来使用该框架处理同步请求。
SyncAdapter适用于需要同步本地数据和在线账户信息的应用,如电子邮件的定时收取、笔记应用的云备份、天气应用的及时同步等。SyncAdapter设计为必须与用户账户绑定,即使你的应用不需要账户认证,也需要实现相关的类来处理账户,并可以将其隐藏。
我发现数据同步管理比较复杂,且参考资料不多,这里我对其进行简单的梳理。主要是从遇到的问题来进行分析的。
【问题描述】:
下拉快捷中有同步按钮,默认是关闭状态,之后使用 wifi 万能钥匙,发现该开关自动打开了。
1.2 调用流程
这里是根据问题进行代码跟踪的,我的关注点在下拉状态栏的同步按钮为什么会自动打开。研究发现,这里的开关主要是调用setMasterSyncAutomatically() 方法。
ContentResolver.java
setMasterSyncAutomatically()
ContentService.java
setMasterSyncAutomatically()
SyncManager.java
setMasterSyncAutomatically()
SyncStorageEngine.java
setMasterSyncAutomatically()
// mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
// frameworks/base/services/core/java/com/android/server/content/ContentService.java
public void requestSync(Account account, String authority, Bundle extras,
596 String callingPackage) {
597 Bundle.setDefusable(extras, true);
598 ContentResolver.validateSyncExtrasBundle(extras);
599 int userId = UserHandle.getCallingUserId();
600 final int callingUid = Binder.getCallingUid();
601 final int callingPid = Binder.getCallingPid();
602
603 validateExtras(callingUid, extras);
604 final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callingUid, extras);
605
606 // This makes it so that future permission checks will be in the context of this
607 // process rather than the caller's process. We will restore this before returning.
608 final long identityToken = clearCallingIdentity();
609 try {
610 SyncManager syncManager = getSyncManager();
611 if (syncManager != null) {
// 该处调用到 syncManager
612 syncManager.scheduleSync(account, userId, callingUid, authority, extras,
613 SyncStorageEngine.AuthorityInfo.UNDEFINED,
614 syncExemption, callingUid, callingPid, callingPackage);
615 }
616 } finally {
617 restoreCallingIdentity(identityToken);
618 }
619 }
当同步模式改变时,系统会向外界发送广播 com.android.sync.SYNC_CONN_STATUS_CHANGED
setting 点击快捷时,可以调用 ContentResolver.setMasterSyncAutomaticallyAsUser() 方法来设置同步状态。
packages/apps/Settings/src/com/android/settings/users/AutoSyncDataPreferenceController.java
public void onClick(DialogInterface dialog, int which) {
155 if (which == DialogInterface.BUTTON_POSITIVE) {
156 ContentResolver.setMasterSyncAutomaticallyAsUser(mEnabling,
157 mUserHandle.getIdentifier());
158 if (mPreference != null) {
159 mPreference.setChecked(mEnabling);
160 }
161 }
162 }
163 }
1.2.1 同步判断条件
Android 同步框架发起同步判断条件,所有同步发起时会判断以下属性:
-
Provider 的 isSyncable
-
SyncAdapter 的 isAlwaysSyncable
-
自动同步(SYNC_EXTRAS_MANUAL 为 false) 发起时除以上之外,还要判断:
- 系统总同步开关(getMasterSyncAutomatically 获取系统总同步开关状态 )
- SyncAdapter 同步开关( getSyncAutomatically )
如果是通过调用 ContentResolver 的 notifyChange 发起自动同步时会带 SYNC_EXTRAS_UPLOAD 标志。
Android设计原意是仅将本地数据更新至服务端。此时SyncAdapter中的supportsUploading若是false,则不能发起自动同步。
1.2.2 getMasterSyncAutomatically()
我们这里看看 getMasterSyncAutomatically 这个方法,主要是获取系统级的总同步开关。
frameworks/base/core/java/android/content/ContentResolver.java
3453 /**
3454 * Gets the global auto-sync setting that applies to all the providers and accounts.
3455 * If this is false then the per-provider auto-sync setting is ignored.
3456 * <p>This method requires the caller to hold the permission
3457 * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
3458 *
3459 * @return the global auto-sync setting that applies to all the providers and accounts
3460 */
3461 public static boolean getMasterSyncAutomatically() {
3462 try {
3463 return getContentService().getMasterSyncAutomatically();
3464 } catch (RemoteException e) {
3465 throw e.rethrowFromSystemServer();
3466 }
3467 }
frameworks/base/services/core/java/com/android/server/content/ContentService.java
// 进一步调用到了这里
1040 public boolean getMasterSyncAutomatically() {
1041 return getMasterSyncAutomaticallyAsUser(UserHandle.getCallingUserId());
1042 }
1043
1044 /**
1045 * If the user id supplied is different to the calling user, the caller must hold the
1046 * INTERACT_ACROSS_USERS_FULL permission.
1047 */
1048 @Override
1049 public boolean getMasterSyncAutomaticallyAsUser(int userId) {
1050 enforceCrossUserPermission(userId,
1051 "no permission to read the sync settings for user " + userId);
1052 mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
1053 "no permission to read the sync settings");
1054
1055 final long identityToken = clearCallingIdentity();
1056 try {
1057 SyncManager syncManager = getSyncManager();
1058 if (syncManager != null) {
// 真正的实现是在 syncStorageEngine 中
1059 return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
1060 }
1061 } finally {
1062 restoreCallingIdentity(identityToken);
1063 }
1064 return false;
1065 }
1066
//frameworks/base/services/core/java/com/android/server/content/SyncStorageEngine.java
public boolean getMasterSyncAutomatically(int userId) {
// 这里 mAuthotities 为权限,同步锁
997 synchronized (mAuthorities) {
998 Boolean auto = mMasterSyncAutomatically.get(userId);
999 return auto == null ? mDefaultMasterSyncAutomatically : auto;
1000 }
1001 }
// private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>(); 布尔型的数组
1.2.3 setMasterSyncAutomatically()
这个方法是核心方法,主要用来设置同步开关,层层调用最后在 SyncStorageEngine 中实现。
// 该处同步状态发生改变的时候会调用,设置同步开关
973 public void setMasterSyncAutomatically(boolean flag, int userId,
974 @SyncExemption int syncExemptionFlag, int callingUid, int callingPid) {
975 mLogger.log("Set master enabled=", flag, " user=", userId,
976 " cuid=", callingUid,
977 " cpid=", callingPid);
978 synchronized (mAuthorities) {
979 Boolean auto = mMasterSyncAutomatically.get(userId);
// 状态不变时退出,不做改变
980 if (auto != null && auto.equals(flag)) {
981 return;
982 }
// 往该数组中写入变更后的状态
983 mMasterSyncAutomatically.put(userId, flag);
984 writeAccountInfoLocked();
985 }
986 if (flag) {
// 做进一步的同步操作
987 requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null,
988 new Bundle(),
989 syncExemptionFlag, callingUid, callingPid);
990 }
991 reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
// 发送同步状态变化的广播
992 mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
993 queueBackup();
994 }
// 通过堆栈打印,这里是进行 Binder 通信
06-28 10:49:17.995 2413 3536 D SyncManager: java.lang.Throwable
06-28 10:49:17.995 2413 3536 D SyncManager: at com.android.server.content.SyncStorageEngine.setMasterSyncAutomatically(SyncStorageEngine.java:975)
06-28 10:49:17.995 2413 3536 D SyncManager: at com.android.server.content.ContentService.setMasterSyncAutomaticallyAsUser(ContentService.java:1087)
06-28 10:49:17.995 2413 3536 D SyncManager: at android.content.IContentService$Stub.onTransact(IContentService.java:866)
06-28 10:49:17.995 2413 3536 D SyncManager: at android.os.Binder.execTransactInternal(Binder.java:1179)
06-28 10:49:17.995 2413 3536 D SyncManager: at android.os.Binder.execTransact(Binder.java:1143)
这里应用调用 setMasterSyncAutomatically() 方法的话,是可以改变状态的。如果传过来的 flag 为 true 那么,同步开关就会打开。
ContentResolver.setMasterSyncAutomatically(true)
另外我们看看 mAuthorities, 它是所有可同步权限的列表,也是我们的同步锁。
434 // Primary list of all syncable authorities. Also our global lock.
435 @VisibleForTesting
436 final SparseArray<AuthorityInfo> mAuthorities =
437 new SparseArray<AuthorityInfo>();
1.3 问题分析
【问题描述】: 用户在使用wifi万能钥匙等app应用时,之前默认关闭的同步开关,这时自动打开。
【分析过程】:
- 我在分析中发现,第一次启动 wifi 万能钥匙,这时同步开关会自动打开。
- 如果不杀 wifi 万能钥匙的后台,手动关闭同步开关,再次使用 WiFi 万能钥匙,则不会再自动打开。
- 杀掉 wifi 万能钥匙后再打开,这时同步开关会自动打开。
打开wifi万能钥匙的堆栈打印:
我们看mAuthorities 这里的打印,可以达到 wifi 万能钥匙的 mAuthorities 中的条件是满足的,且申请同步,所以这里设置为 true的,于是同步开关就打开了。但每个应用的表现不一样,应该是 wifi 万能钥匙中有调用ContentResolver.setMasterSyncAutomatically(true),第一次打开时想要进行数据同步。
06-28 11:39:44.778 2632 3597 D SyncManager: java.lang.Throwable
06-28 11:39:44.778 2632 3597 D SyncManager: at com.android.server.content.SyncStorageEngine.setMasterSyncAutomatically(SyncStorageEngine.java:975)
06-28 11:39:44.778 2632 3597 D SyncManager: at com.android.server.content.ContentService.setMasterSyncAutomaticallyAsUser(ContentService.java:1087)
06-28 11:39:44.778 2632 3597 D SyncManager: at android.content.IContentService$Stub.onTransact(IContentService.java:866)
06-28 11:39:44.778 2632 3597 D SyncManager: at android.os.Binder.execTransactInternal(Binder.java:1179)
06-28 11:39:44.778 2632 3597 D SyncManager: at android.os.Binder.execTransact(Binder.java:1143)
06-28 11:39:44.778 2632 3597 D SyncManager: Set master enabled=true user=0 cuid=10355 cpid=16799
// 这里我们可以达到 wifi 万能钥匙的 mAuthorities 中的条件是满足的,且申请同步,所以这里设置为 true的,于是同步开关就打开了
06-28 11:39:44.778 2632 3597 D SyncManager: mAuthorities{1=用户8133096253497/com.ss.android.ugc.aweme.AccountSyncProvider:u0, enabled=false, syncable=0, backoff=-1, delay=0, 2=sync.wifi.com/com.lantern.daemon.data.sync:u0, enabled=true,
syncable=1, backoff=444635, delay=0, 3=account.wifi.com/com.lantern.daemon.data.sync.one:u0, enabled=true, syncable=1, backoff=445016, delay=0, 4=wifi.com/com.snda.wifilocating.sync.provider:u0, enabled=true, syncable=1, backoff=-1, delay=0, 5=accounts.wifi.com/com.lantern.daemon.data.sync.new:u0, enabled=true, syncable=-1, backoff=-1, delay=0}
06-28 11:39:44.783 2632 3597 D SyncManager: java.lang.Throwable
06-28 11:39:44.783 2632 3597 D SyncManager: at com.android.server.content.SyncStorageEngine.getMasterSyncAutomatically(SyncStorageEngine.java:999)
06-28 11:39:44.783 2632 3597 D SyncManager: at com.android.server.content.SyncManager.scheduleSync(SyncManager.java:1048)
06-28 11:39:44.783 2632 3597 D SyncManager: at com.android.server.content.SyncManager.scheduleSync(SyncManager.java:886)
06-28 11:39:44.783 2632 3597 D SyncManager: at com.android.server.content.SyncManager$7.onSyncRequest(SyncManager.java:578)
06-28 11:39:44.783 2632 3597 D SyncManager: at com.android.server.content.SyncStorageEngine.requestSync(SyncStorageEngine.java:2391)
06-28 11:39:44.783 2632 3597 D SyncManager: at com.android.server.content.SyncStorageEngine.setMasterSyncAutomatically(SyncStorageEngine.java:989)
06-28 11:39:44.783 2632 3597 D SyncManager: at com.android.server.content.ContentService.setMasterSyncAutomaticallyAsUser(ContentService.java:1087)
06-28 11:39:44.783 2632 3597 D SyncManager: at android.content.IContentService$Stub.onTransact(IContentService.java:866)
06-28 11:39:44.783 2632 3597 D SyncManager: at android.os.Binder.execTransactInternal(Binder.java:1179)
06-28 11:39:44.783 2632 3597 D SyncManager: at android.os.Binder.execTransact(Binder.java:1143)
06-28 11:39:44.783 2632 3597 D SyncManager: mMasterSyncAutomatically{0=true}autotrue
关于 ContentService 、SyncManager 跟详细的分析请看下一篇(未完待续)......