1718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana/*
2718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * Copyright (C) 2009 The Android Open Source Project
3718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana *
4718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * Licensed under the Apache License, Version 2.0 (the "License");
5718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * you may not use this file except in compliance with the License.
6718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * You may obtain a copy of the License at
7718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana *
8718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana *      http://www.apache.org/licenses/LICENSE-2.0
9718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana *
10718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * Unless required by applicable law or agreed to in writing, software
11718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * distributed under the License is distributed on an "AS IS" BASIS,
12718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * See the License for the specific language governing permissions and
14718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * limitations under the License.
15718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana */
16718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
17718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanapackage android.content;
18718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
19718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.pm.RegisteredServicesCache;
205ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport android.content.pm.XmlSerializerAndParser;
2120cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackbornimport android.content.res.Resources;
22718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.content.res.TypedArray;
2383b66b2289a103cc6563cc54f9d54632fd64bffbSuprabh Shuklaimport android.text.TextUtils;
2437a40c24deb02bca3868a8085069afae112f22e4Amith Yamasaniimport android.util.ArrayMap;
25718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintanaimport android.util.AttributeSet;
2637a40c24deb02bca3868a8085069afae112f22e4Amith Yamasaniimport android.util.SparseArray;
2737a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
2837a40c24deb02bca3868a8085069afae112f22e4Amith Yamasaniimport com.android.internal.annotations.GuardedBy;
2937a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
305ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport org.xmlpull.v1.XmlPullParser;
315ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport org.xmlpull.v1.XmlSerializer;
325ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport org.xmlpull.v1.XmlPullParserException;
335ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
345ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintanaimport java.io.IOException;
3537a40c24deb02bca3868a8085069afae112f22e4Amith Yamasaniimport java.util.ArrayList;
3637a40c24deb02bca3868a8085069afae112f22e4Amith Yamasaniimport java.util.Collection;
37718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
38718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana/**
39718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * A cache of services that export the {@link android.content.ISyncAdapter} interface.
40718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana * @hide
41718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana */
427a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkeypublic class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> {
43718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private static final String TAG = "Account";
44718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
45718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
46718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
47718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    private static final String ATTRIBUTES_NAME = "sync-adapter";
485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    private static final MySerializer sSerializer = new MySerializer();
49718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
5037a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    @GuardedBy("mServicesLock")
5137a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    private SparseArray<ArrayMap<String,String[]>> mAuthorityToSyncAdapters
5237a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            = new SparseArray<>();
5337a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
547a96c39c510923ef73bbb06ab20109f0168b8eb1Jeff Sharkey    public SyncAdaptersCache(Context context) {
555ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer);
56718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
57718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana
5820cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn    public SyncAdapterType parseServiceAttributes(Resources res,
5920cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn            String packageName, AttributeSet attrs) {
6020cb56e26e91df91bd64d4251222e0d421cdbe47Dianne Hackborn        TypedArray sa = res.obtainAttributes(attrs,
61718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                com.android.internal.R.styleable.SyncAdapter);
62718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        try {
63718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            final String authority =
64718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                    sa.getString(com.android.internal.R.styleable.SyncAdapter_contentAuthority);
65718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            final String accountType =
66718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                    sa.getString(com.android.internal.R.styleable.SyncAdapter_accountType);
6783b66b2289a103cc6563cc54f9d54632fd64bffbSuprabh Shukla            if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(accountType)) {
68718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana                return null;
69718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            }
704a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana            final boolean userVisible =
714a6679b97e0285c5b65ec5c0d9080ff90d3e9e81Fred Quintana                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_userVisible, true);
72e0616ffb741b64e3bc7a1e3ad9def3d50eee53fdFred Quintana            final boolean supportsUploading =
73e0616ffb741b64e3bc7a1e3ad9def3d50eee53fdFred Quintana                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_supportsUploading,
74e0616ffb741b64e3bc7a1e3ad9def3d50eee53fdFred Quintana                            true);
750c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            final boolean isAlwaysSyncable =
760c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_isAlwaysSyncable,
770c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                            false);
780c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            final boolean allowParallelSyncs =
790c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                    sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_allowParallelSyncs,
800c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana                            false);
81e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana            final String settingsActivity =
82e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana                    sa.getString(com.android.internal.R.styleable
83e6d60ecdf668499f003a81274f18cb57075eb65bFred Quintana                            .SyncAdapter_settingsActivity);
840c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana            return new SyncAdapterType(authority, accountType, userVisible, supportsUploading,
855cb2973495084f8ce3433b579e4b4962ed9d7efcSvetoslav Ganov                    isAlwaysSyncable, allowParallelSyncs, settingsActivity, packageName);
86718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        } finally {
87718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana            sa.recycle();
88718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana        }
89718d8a2d7ff3e864a73879eb646f46c14ab74d07Fred Quintana    }
905ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana
9137a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    @Override
9237a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    protected void onServicesChangedLocked(int userId) {
9337a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani        synchronized (mServicesLock) {
9437a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            ArrayMap<String,String[]> adapterMap = mAuthorityToSyncAdapters.get(userId);
9537a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            if (adapterMap != null) {
9637a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani                adapterMap.clear();
9737a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            }
9837a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani        }
9937a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
10037a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani        super.onServicesChangedLocked(userId);
10137a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    }
10237a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
10337a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    public String[] getSyncAdapterPackagesForAuthority(String authority, int userId) {
10437a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani        synchronized (mServicesLock) {
10537a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            ArrayMap<String,String[]> adapterMap = mAuthorityToSyncAdapters.get(userId);
10637a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            if (adapterMap == null) {
10737a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani                adapterMap = new ArrayMap<>();
10837a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani                mAuthorityToSyncAdapters.put(userId, adapterMap);
10937a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            }
11037a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            // If the mapping exists, return it
11137a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            if (adapterMap.containsKey(authority)) {
11237a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani                return adapterMap.get(authority);
11337a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            }
11437a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            // Create the mapping and cache it
11537a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            String[] syncAdapterPackages;
11637a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
11737a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            serviceInfos = getAllServices(userId);
11837a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            ArrayList<String> packages = new ArrayList<>();
11937a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
12037a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani                if (authority.equals(serviceInfo.type.authority)
12137a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani                        && serviceInfo.componentName != null) {
12237a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani                    packages.add(serviceInfo.componentName.getPackageName());
12337a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani                }
12437a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            }
12537a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            syncAdapterPackages = new String[packages.size()];
12637a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            packages.toArray(syncAdapterPackages);
12737a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            adapterMap.put(authority, syncAdapterPackages);
12837a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
12937a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            return syncAdapterPackages;
13037a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani        }
13137a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    }
13237a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
13337a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    @Override
13437a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    protected void onUserRemoved(int userId) {
13537a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani        synchronized (mServicesLock) {
13637a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani            mAuthorityToSyncAdapters.remove(userId);
13737a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani        }
13837a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
13937a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani        super.onUserRemoved(userId);
14037a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani    }
14137a40c24deb02bca3868a8085069afae112f22e4Amith Yamasani
1425ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    static class MySerializer implements XmlSerializerAndParser<SyncAdapterType> {
1435ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException {
1445ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.attribute(null, "authority", item.authority);
1455ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            out.attribute(null, "accountType", item.accountType);
1465ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1470c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana
1485ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        public SyncAdapterType createFromXml(XmlPullParser parser)
1495ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana                throws IOException, XmlPullParserException {
1505ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            final String authority = parser.getAttributeValue(null, "authority");
1515ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            final String accountType = parser.getAttributeValue(null, "accountType");
1525ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana            return SyncAdapterType.newKey(authority, accountType);
1535ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana        }
1545ebbb4a6b3e16f711735ae0615b9a9ea64faad38Fred Quintana    }
1550c4d04ac2e8aa62560d8d767fa1c87e5361b0b08Fred Quintana}
156