[SystemServer] AccountManagerService 浅析

前言

这篇文章主要简单分析下AccountManagerService,首先我们要有个基本的认识,就是AccountManagerSrevice是干什么的?下意识的会觉得这是个管理用户的service,其实不然,AccountManagerService其实是用来管理"账户"的,比如你的Email,微博,Facebook账户,而用户在账户之上,用户含有账户。

有了以上的基本共识,我们来开始看下源码:

源码

在SystemServer的run方法中启动services

      // Start services.
        try {
            traceBeginAndSlog("StartServices")
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            SystemServerInitThreadPool.shutdown();
        }

AccountManagerService位于startOtherServices()方法中,可见其并不是系统的关键服务,startOtherServices中启动AccountManagerService的代码如下:

            // The AccountManager must come before the ContentService
            traceBeginAndSlog("StartAccountManagerService");
            mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS);
            traceEnd();

其中ACCOUNT_SERVICE_CLASS为:

    private static final String ACCOUNT_SERVICE_CLASS =
            "com.android.server.accounts.AccountManagerService$Lifecycle";

可以看到,真正启动的service封装其实是AccountManagerService中Lifecycle内部类,这种方式是SystemServer中启动services的通用方法。而在SystemServiceManager类中,经过系列转换是回掉类型位SystemService的onStart方法,可见Lifecycle内部类肯定是SystemService的子类了。

    public void startService(@NonNull final SystemService service) {
        // Register it.
        mServices.add(service);
        // Start it.
        long time = SystemClock.elapsedRealtime();
        try {
            //回掉service的onStart方法开始启动Service
            service.onStart();
        } catch (RuntimeException ex) {
            throw new RuntimeException("Failed to start service " + service.getClass().getName()
                    + ": onStart threw an exception", ex);
        }
        warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
    }

再看AccountManagerService中的实现,首先是Lifecycle内部类:

    public static class Lifecycle extends SystemService {
        private AccountManagerService mService;
        public Lifecycle(Context context) {
            super(context);
        }
        @Override
        public void onStart() {
            mService = new AccountManagerService(new Injector(getContext()));
            publishBinderService(Context.ACCOUNT_SERVICE, mService);
        }
        @Override
        public void onUnlockUser(int userHandle) {
            mService.onUnlockUser(userHandle);
        }
        @Override
        public void onStopUser(int userHandle) {
            Slog.i(TAG, "onStopUser " + userHandle);
            mService.purgeUserData(userHandle);
        }
    }

果然继承了SystemService,前面讲到启动SystemService是通过会调onStart方法的形式,那么AccountManagerService中的onStart做了什么:

        @Override
        public void onStart() {
            mService = new AccountManagerService(new Injector(getContext()));
            publishBinderService(Context.ACCOUNT_SERVICE, mService);
        }

在这里publishBinderService主要是将这个service添加到ServiceManager这个服务管家中,那么AccountManagerService初始化的关键就是它的构造函数了,首先是Injector类:

    static class Injector {
        private final Context mContext;

        public Injector(Context context) {
            mContext = context;
        }

        //这里会启动一个线程,并返回其looper
        Looper getMessageHandlerLooper() {
            ServiceThread serviceThread = new ServiceThread(TAG,
                    android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
            serviceThread.start();
            return serviceThread.getLooper();
        }

        Context getContext() {
            return mContext;
        }
             //添加服务到LocalServices中
        void addLocalService(AccountManagerInternal service) {
            LocalServices.addService(AccountManagerInternal.class, service);
        }
               //获取设备加密存储数据库名称
        String getDeDatabaseName(int userId) {
            File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
                    AccountsDb.DE_DATABASE_NAME);
            return databaseFile.getPath();
        }
               //获取凭据加密存储数据库名称
        String getCeDatabaseName(int userId) {
            File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
                    AccountsDb.CE_DATABASE_NAME);
            return databaseFile.getPath();
        }
               //获取Android N之前的数据库名称
        String getPreNDatabaseName(int userId) {
            File systemDir = Environment.getDataSystemDirectory();
            File databaseFile = new File(Environment.getUserSystemDirectory(userId),
                    PRE_N_DATABASE_NAME);
            if (userId == 0) {
                // Migrate old file, if it exists, to the new location.
                // Make sure the new file doesn't already exist. A dummy file could have been
                // accidentally created in the old location,
                // causing the new one to become corrupted as well.
                File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
                if (oldFile.exists() && !databaseFile.exists()) {
                    // Check for use directory; create if it doesn't exist, else renameTo will fail
                    File userDir = Environment.getUserSystemDirectory(userId);
                    if (!userDir.exists()) {
                        if (!userDir.mkdirs()) {
                            throw new IllegalStateException(
                                    "User dir cannot be created: " + userDir);
                        }
                    }
                    if (!oldFile.renameTo(databaseFile)) {
                        throw new IllegalStateException(
                                "User dir cannot be migrated: " + databaseFile);
                    }
                }
            }
            return databaseFile.getPath();
        }

        IAccountAuthenticatorCache getAccountAuthenticatorCache() {
            return new AccountAuthenticatorCache(mContext);
        }

        INotificationManager getNotificationManager() {
            return NotificationManager.getService();
        }
    }

从Injector类的代码可以看出,它主要是创建了一个线程,提供了获取几个数据库名称的api接口。值得一提的是这里的两个数据库:credential encryption database 和 device encryption database,它们代表了每个用户在Android N之后的可使用的加密存储位置:
1、CE(credential encryption)凭据加密存储空间:默认存储空间,只有在用户解锁设备后才可用,对应存储位置:/data/system_ce/0/accounts_ce.db
2、DE(device encryption)设备加密存储空间:在direct root模式下以及用户解锁设备后均可用,对应存储位置:/data/system_de/0/accounts_de.db
这里的direct root代表用户开机但是还没解锁。而在Android N之前没有de和ce的区别,加密存储位置为:/data/system/users/0/accounts.db

我们继续看AccountManagerService的构造函数:

public AccountManagerService(Injector injector) {
        mInjector = injector;
        mContext = injector.getContext();
        mPackageManager = mContext.getPackageManager();
        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
        // 这里通过injector启动线程并且创建Handler实例
        mHandler = new MessageHandler(injector.getMessageHandlerLooper());
        mAuthenticatorCache = mInjector.getAccountAuthenticatorCache();
        mAuthenticatorCache.setListener(this, null /* Handler */);

        sThis.set(this);
        // 注册监听器监听PACKAGE_REMOVED事件,以在必要时清空相应数据。
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addDataScheme("package");
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context1, Intent intent) {
                // Don't delete accounts when updating a authenticator's
                // package.
                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                    final String removedPackageName = intent.getData().getSchemeSpecificPart();
                    Runnable purgingRunnable = new Runnable() {
                        @Override
                        public void run() {
                            purgeOldGrantsAll();
                            // Notify authenticator about removed app?
                            removeVisibilityValuesForPackage(removedPackageName);
                        }
                    };
                    mHandler.post(purgingRunnable);
                }
            }
        }, intentFilter);

        injector.addLocalService(new AccountManagerInternalImpl());
        // 注册监听器,监听USER_REMOVED事件,以在必要时清楚用户数据
        IntentFilter userFilter = new IntentFilter();
        userFilter.addAction(Intent.ACTION_USER_REMOVED);
        mContext.registerReceiverAsUser(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (Intent.ACTION_USER_REMOVED.equals(action)) {
                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                    if (userId < 1) return;
                    Slog.i(TAG, "User " + userId + " removed");
                    purgeUserData(userId);
                }
            }
        }, UserHandle.ALL, userFilter, null, null);
        //...
    }

从其构造函数来看AccountManagerService主要是通过Injector初始化了一些实例,并且监听了一些事件。回到AccountManagerService的本质,作为FrameWork 中的一个系统service,我们可以思考一下这些service是怎么工作的?我想来想去:下面两种可能是主要的工作模式:

1、通过进程间或是线程间通信接收来自客户端的消息,处理并返回。

2、通过实现会调方法或是监听广播事件,在必要时刻做些工作。

那么这样的职能和可能涉及多个模块的交互,如何讲明白,其实我觉得画图是很好的方法。

我个人偏好画一些类图和结构图,因为框架性的类设计和结构画清楚了,对其的运作也就了解的差不多了,如果在wiki中,这样的图就尤其适合了。

前面看了AccountManagerService在系统中的位置及其初始化阶段都做了哪些事情,下面根据一张图,我们来看看它的强关系及它是如何与其它相关的类关联起来的:


AccountManagerService

从AccountManagerService的强关系中,我们可以看到它继承了IAccountManager.Stub是IAccountManager的Bn端,实现了RegisteredServicesCacheListener,同时它位于framework的com.android.server的包中。 那么按照我们我们之前讲的,位于另一个包中的AccountManager因为持有IAccountManager Bp端(见图中的红色proxy字体),它会向AccountManagerService发送消息,而又实现了RegisteredServicesCacheListener,说明RegisteredServicesCacheListener中的方法也会在某一个时机回掉到AccountManagerService中。

这样的设计看起来很常见,也好理解。那AccountManager都向AccountManagerService发了什么样的消息呢? 我们从AccountManager的功能出发,作为一个提供账户管理的服务,增删账户肯定是基础功能。下面从增加账户作为切入口:

账户管理

1、AccountManager 新起一个异步Task, 通过Binder调用到AccountManagerService中的内部类Session,将AccountManager中的Response实例作为变量传递过去以备后面通知结果。

 new AmsTask(activity, handler, callback) {
            @Override
            public void doWork() throws RemoteException {
                mService.addAccount(mResponse, accountType, authTokenType,
                        requiredFeatures, activity != null, optionsIn);
            }
        }.start();

2、Session本身是IAccountAuthenticatorResponse的Bn端,同时内部持有IAccountAuthenticator的Bp端
Session部分代码:

// 继承IAccountAuthenticatorResponse.Stub
    private abstract class Session extends IAccountAuthenticatorResponse.Stub
            implements IBinder.DeathRecipient, ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        // 持有IAccountAuthenticator的Bp端
            mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
            try {
                run();
            } catch (RemoteException e) {
                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
                        "remote exception");
            }
        }

3、Session通过调用IAccountAuthenticator的Bp接口,让AccountAuthenticator完成实际的添加账户的动作。

new Session(accounts, response, accountType, expectActivityLaunch,
                    true /* stripAuthTokenFromResult */, null /* accountName */,
                    false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
                @Override
                public void run() throws RemoteException {
                // Session调用IAccountAuthenticator的bp接口
                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
                            options);
                }

                @Override
                protected String toDebugString(long now) {
                    return super.toDebugString(now) + ", addAccount"
                            + ", accountType " + accountType
                            + ", requiredFeatures " + Arrays.toString(requiredFeatures);
                }
            }.bind();

4、AccountAuthenticator 完成后通过IAccountAuthenticatorResponse Bp端通知其Bn端也就是Session,Session 再通过2步的Response实例通知回AccountManager。

注意图中Session类所在的一个闭环。

数据库操作

前面我们提过AccountManagerService 实现了RegisteredServicesCacheListener接口,那么此接口中的方法肯定会在一定条件下回调到AccountManagerService中。而且值得注意的是RegisteredServicesCacheListener 和图中的RegisteredServicesCache都在pm包中,引人思考。

1、Inject类创建AccountAuthenticatorCache实例,AccountAuthenticatorCache实例setListener

 mAuthenticatorCache = mInjector.getAccountAuthenticatorCache();
        mAuthenticatorCache.setListener(this, null /* Handler */);

2、AccountAuthenticatorCache继承的RegisteredServicesCache在监听package的add remove等事件。

RegisteredServicesCache的构造函数内:
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addDataScheme("package");
        mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);

        // Register for events related to sdcard installation.
        IntentFilter sdFilter = new IntentFilter();
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        mContext.registerReceiver(mExternalReceiver, sdFilter);

3、一旦package的add remove发生,RegisteredServicesCache开始回调Listener,AccountManagerService中的实现被调用

//RegisteredServicesCache 内通知Listener
        handler.post(new Runnable() {
            public void run() {
                listener2.onServiceChanged(type, userId, removed);
            }
        });

//AccountManagerService中的实现
    @Override
    public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
        validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
    }

4、AccountManagerService中实现的方法是在更新用户数据库。

private void validateAccountsInternal(
            UserAccounts accounts, boolean invalidateAuthenticatorCache) {

        if (invalidateAuthenticatorCache) {
            mAuthenticatorCache.invalidateCache(accounts.userId);
        }

        final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
                mAuthenticatorCache, accounts.userId);
        boolean userUnlocked = isLocalUnlockedUser(accounts.userId);

        synchronized (accounts.dbLock) {
            synchronized (accounts.cacheLock) {
                boolean accountDeleted = false;
               // 获取DB实例
               final AccountsDb accountsDb = accounts.accountsDb;
                Map<String, Integer> metaAuthUid = accountsDb.findMetaAuthUid();
                // Create a list of authenticator type whose previous uid no longer exists
                HashSet<String> obsoleteAuthType = Sets.newHashSet();
                SparseBooleanArray knownUids = null;
                for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
                    String type = authToUidEntry.getKey();
                    int uid = authToUidEntry.getValue();
                    Integer knownUid = knownAuth.get(type);
                    if (knownUid != null && uid == knownUid) {
                        // Remove it from the knownAuth list if it's unchanged.
                        knownAuth.remove(type);
                    } else {
                        if (knownUids == null) {
                            knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
                        }
                        if (!knownUids.get(uid)) {
                            // The authenticator is not presently available to the cache. And the
                            // package no longer has a data directory (so we surmise it isn't
                            // updating). So purge its data from the account databases.
                            obsoleteAuthType.add(type);
                            //DB 操作                            
                            accountsDb.deleteMetaByAuthTypeAndUid(type, uid);
                        }
                    }
                }

               for (Entry<String, Integer> entry : knownAuth.entrySet()) {
                     // DB操作
                    accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
                }

                final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
                try {
                    accounts.accountCache.clear();
                    final HashMap<String, ArrayList<String>> accountNamesByType
                            = new LinkedHashMap<>();
                    for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
                        final long accountId = accountEntry.getKey();
                        final Account account = accountEntry.getValue();
                        if (obsoleteAuthType.contains(account.type)) {
                           Map<String, Integer> packagesToVisibility =
                                    getRequestingPackages(account, accounts);
                            List<String> accountRemovedReceivers =
                                getAccountRemovedReceivers(account, accounts);
                            // DB操作
                            accountsDb.beginTransaction();
                            try {
                                accountsDb.deleteDeAccount(accountId);
                               if (userUnlocked) {
                                    accountsDb.deleteCeAccount(accountId);
                                }
                                accountsDb.setTransactionSuccessful();
                            } finally {
                                accountsDb.endTransaction();
                            }
                            accountDeleted = true;

                            logRecord(AccountsDb.DEBUG_ACTION_AUTHENTICATOR_REMOVE,
                                    AccountsDb.TABLE_ACCOUNTS, accountId, accounts);

                            accounts.userDataCache.remove(account);
                            accounts.authTokenCache.remove(account);
                            accounts.accountTokenCaches.remove(account);
                            accounts.visibilityCache.remove(account);

                            for (Entry<String, Integer> packageToVisibility :
                                    packagesToVisibility.entrySet()) {
                                if (isVisible(packageToVisibility.getValue())) {
                                    notifyPackage(packageToVisibility.getKey(), accounts);
                                }
                            }
                            for (String packageName : accountRemovedReceivers) {
                                sendAccountRemovedBroadcast(account, packageName, accounts.userId);
                            }
                        } else {
                            ArrayList<String> accountNames = accountNamesByType.get(account.type);
                            if (accountNames == null) {
                                accountNames = new ArrayList<>();
                                accountNamesByType.put(account.type, accountNames);
                            }
                            accountNames.add(account.name);
                        }
                    }
                    for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
                        final String accountType = cur.getKey();
                        final ArrayList<String> accountNames = cur.getValue();
                        final Account[] accountsForType = new Account[accountNames.size()];
                        for (int i = 0; i < accountsForType.length; i++) {
                            accountsForType[i] = new Account(accountNames.get(i), accountType,
                                    UUID.randomUUID().toString());
                        }
                        accounts.accountCache.put(accountType, accountsForType);
                    }
                    accounts.visibilityCache.putAll(accountsDb.findAllVisibilityValues());
                } finally {
                    if (accountDeleted) {
                        sendAccountsChangedBroadcast(accounts.userId);
                    }
                }
            }
        }
    }

注意图中AccountAuthenticatorCache所在的一个闭环

总结

AccountManagerService作为一个轻量级service,总得来说比较简单。但是他对数据库的频繁操作(触发条件比较容易),容易让它为用户目录或用户管理发生的错误背锅,错误不是它造成的,但是有大几率出现在它这。后续在分析问题案例和service时还会提到它。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • 哈哈
    破旧脾气阅读 206评论 0 0
  • 郑璐 宜昌 焦点网络中级七期 原创持续分享第135天 今天中午和邹老师、林老师地面约练了一次,因为在同一个地方,...
    迷你旅客阅读 137评论 0 0
  • 2019年1月7日 星期一 天气:毛毛雨、冷 今天放学妈妈和她的好朋友小凤阿姨一起来接我去吃饭,我很开心。...
    阮滢阅读 201评论 0 2
  • 微博讨论左派右派,我也关注了这个概念。我发现我身上有很多左派作风,这些习性让我痛苦不已。改变是良药,只是改变之前找...
    鹦鹉红旺阅读 237评论 0 0