19fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki/*
29fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * Copyright (C) 2010 The Android Open Source Project
39fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki *
49fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
59fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * you may not use this file except in compliance with the License.
69fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * You may obtain a copy of the License at
79fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki *
89fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
99fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki *
109fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * Unless required by applicable law or agreed to in writing, software
119fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
129fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * See the License for the specific language governing permissions and
149fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki * limitations under the License.
159fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki */
169fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki
1766a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankpackage com.android.email.service;
189fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki
1970edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.accounts.AccountManager;
2002f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Huimport android.accounts.AccountManagerCallback;
2170edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.accounts.AccountManagerFuture;
2270edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.accounts.AuthenticatorException;
2370edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.accounts.OperationCanceledException;
2403cd72805dab0379ed255d151f1c17cc60655fc3Marc Blankimport android.app.Service;
2574dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdonimport android.content.ComponentName;
26e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blankimport android.content.ContentProviderClient;
2770edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.content.ContentResolver;
2870edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.content.ContentUris;
2970edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.content.ContentValues;
309fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onukiimport android.content.Context;
319fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onukiimport android.content.Intent;
323a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantlerimport android.content.pm.ActivityInfo;
3374dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdonimport android.content.pm.PackageManager;
343a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantlerimport android.content.res.Configuration;
35f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.content.res.Resources;
36f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.content.res.TypedArray;
37f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.content.res.XmlResourceParser;
3870edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.database.Cursor;
3970edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.net.Uri;
4003cd72805dab0379ed255d151f1c17cc60655fc3Marc Blankimport android.os.Bundle;
4103cd72805dab0379ed255d151f1c17cc60655fc3Marc Blankimport android.os.IBinder;
4203cd72805dab0379ed255d151f1c17cc60655fc3Marc Blankimport android.os.RemoteException;
4370edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.provider.CalendarContract;
44e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blankimport android.provider.CalendarContract.Calendars;
45e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blankimport android.provider.CalendarContract.SyncState;
4670edcf05387df33f4761b766add6b80999e425e9Marc Blankimport android.provider.ContactsContract;
473aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdonimport android.provider.ContactsContract.RawContacts;
48e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blankimport android.provider.SyncStateContract;
494c865846856cd7530fb51e244a9971a3d5e1fcd8Tony Mantlerimport android.support.annotation.Nullable;
5053b64eee54cdb74dc325089b8362a6ff4811759eTony Mantlerimport android.text.TextUtils;
51e1a6088ee4e3f0e4344dd9bc38029b6d01431eabMarc Blank
52f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.R;
53ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Leeimport com.android.emailcommon.VendorPolicyLoader;
54f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.provider.Account;
5570edcf05387df33f4761b766add6b80999e425e9Marc Blankimport com.android.emailcommon.provider.EmailContent;
5670edcf05387df33f4761b766add6b80999e425e9Marc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns;
573dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantlerimport com.android.emailcommon.provider.EmailContent.HostAuthColumns;
58e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blankimport com.android.emailcommon.provider.HostAuth;
5966a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.emailcommon.service.EmailServiceProxy;
60fb2a3a2f77f954265beaa92b87774006e2d7c185Martin Hibdonimport com.android.emailcommon.service.EmailServiceStatus;
617afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdonimport com.android.emailcommon.service.EmailServiceVersion;
621d8fd9c05449863ac6a151a6db87c267c4f55ac2Martin Hibdonimport com.android.emailcommon.service.HostAuthCompat;
6366a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.emailcommon.service.IEmailService;
6466a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankimport com.android.emailcommon.service.IEmailServiceCallback;
6503cd72805dab0379ed255d151f1c17cc60655fc3Marc Blankimport com.android.emailcommon.service.SearchParams;
6624bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdonimport com.android.emailcommon.service.ServiceProxy;
67f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.service.SyncWindow;
68560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils;
69b5eed31d1bdfcb8e8b316c9c49331295718001bdYu Ping Huimport com.google.common.collect.ImmutableMap;
70462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
71f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport org.xmlpull.v1.XmlPullParserException;
72f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
73f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport java.io.IOException;
74b5eed31d1bdfcb8e8b316c9c49331295718001bdYu Ping Huimport java.util.Collection;
7532881e8dd375f16e6059452166503f7136f14ae5Paul Westbrookimport java.util.Map;
7666a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blank
779fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki/**
7866a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blank * Utility functions for EmailService support.
799fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki */
8066a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blankpublic class EmailServiceUtils {
819fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki    /**
8224bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon     * Ask a service to kill its process. This is used when an account is deleted so that
8324bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon     * no background thread that happens to be running will continue, possibly hitting an
8424bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon     * NPE or other error when trying to operate on an account that no longer exists.
8524bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon     * TODO: This is kind of a hack, it's only needed because we fail so badly if an account
8624bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon     * is deleted out from under us while a sync or other operation is in progress. It would
8724bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon     * be a lot cleaner if our background services could handle this without crashing.
8824bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon     */
8924bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon    public static void killService(Context context, String protocol) {
9024bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon        EmailServiceInfo info = getServiceInfo(context, protocol);
9124bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon        if (info != null && info.intentAction != null) {
9224bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon            final Intent serviceIntent = getServiceIntent(info);
9324bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon            serviceIntent.putExtra(ServiceProxy.EXTRA_FORCE_SHUTDOWN, true);
9424bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon            context.startService(serviceIntent);
9524bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon        }
9624bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon    }
9724bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon
9824bb2dabd9dd7d8bd39fed53f312ae6034e373bbMartin Hibdon    /**
99f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Starts an EmailService by protocol
1009fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki     */
101f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static void startService(Context context, String protocol) {
102f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        EmailServiceInfo info = getServiceInfo(context, protocol);
103f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (info != null && info.intentAction != null) {
104e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook            final Intent serviceIntent = getServiceIntent(info);
105e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook            context.startService(serviceIntent);
106f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
1079fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki    }
1089fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki
1099fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki    /**
110f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Starts all remote services
1119fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki     */
112f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static void startRemoteServices(Context context) {
113f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (EmailServiceInfo info: getServiceInfoList(context)) {
114f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (info.intentAction != null) {
115e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook                final Intent serviceIntent = getServiceIntent(info);
116e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook                context.startService(serviceIntent);
117f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
118f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
119f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
120f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
121f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
122f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Returns whether or not remote services are present on device
123f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
124f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static boolean areRemoteServicesInstalled(Context context) {
125f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (EmailServiceInfo info: getServiceInfoList(context)) {
126f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (info.intentAction != null) {
127f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                return true;
128f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
129f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
130f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return false;
131f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
132f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
133f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
134f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Starts all remote services
135f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
136f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static void setRemoteServicesLogging(Context context, int debugBits) {
137f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (EmailServiceInfo info: getServiceInfoList(context)) {
138f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (info.intentAction != null) {
139f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                EmailServiceProxy service =
1402075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu                        EmailServiceUtils.getService(context, info.protocol);
141f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (service != null) {
142f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    try {
143f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        service.setLogging(debugBits);
144f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } catch (RemoteException e) {
145f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Move along, nothing to see
146f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
147f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
148f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
149f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
1503a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank    }
1513a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank
1523a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank    /**
15366a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blank     * Determine if the EmailService is available
1543a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank     */
155f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static boolean isServiceAvailable(Context context, String protocol) {
156f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        EmailServiceInfo info = getServiceInfo(context, protocol);
157f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (info == null) return false;
158f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (info.klass != null) return true;
159e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook        final Intent serviceIntent = getServiceIntent(info);
160e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook        return new EmailServiceProxy(context, serviceIntent).test();
161e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook    }
162e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook
163e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook    private static Intent getServiceIntent(EmailServiceInfo info) {
164e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook        final Intent serviceIntent = new Intent(info.intentAction);
165e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook        serviceIntent.setPackage(info.intentPackage);
166e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook        return serviceIntent;
167f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
168f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
169f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
170f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * For a given account id, return a service proxy if applicable, or null.
171f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
172f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param accountId the message of interest
1733a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler     * @return service proxy, or null if n/a
174f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
1752075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu    public static EmailServiceProxy getServiceForAccount(Context context, long accountId) {
1762075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu        return getService(context, Account.getProtocol(context, accountId));
177f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
178f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
179f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
180f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Holder of service information (currently just name and class/intent); if there is a class
181f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * member, this is a (local, i.e. same process) service; otherwise, this is a remote service
182f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
183f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static class EmailServiceInfo {
184f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public String protocol;
185f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public String name;
186f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public String accountType;
187f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Class<? extends Service> klass;
188f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String intentAction;
189e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook        String intentPackage;
190f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public int port;
191f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public int portSsl;
192f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean defaultSsl;
193f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean offerTls;
194f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean offerCerts;
1959195a1202483053606f43e871915a5405e955306Martin Hibdon        public boolean offerOAuth;
196f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean usesSmtp;
197f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean offerLocalDeletes;
198f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public int defaultLocalDeletes;
199f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean offerPrefix;
200f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean usesAutodiscover;
201f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean offerLookback;
202f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public int defaultLookback;
203f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean syncChanges;
204f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean syncContacts;
205f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean syncCalendar;
206f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public boolean offerAttachmentPreload;
207f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public CharSequence[] syncIntervalStrings;
208f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public CharSequence[] syncIntervals;
209f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public int defaultSyncInterval;
210b89744ecfb92a4b8fc66a23b942531d39910de44Marc Blank        public String inferPrefix;
211b82ae909d7514bf06090ee3a120aef2154ab0296Marc Blank        public boolean offerLoadMore;
212ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert        public boolean offerMoveTo;
21381b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank        public boolean requiresSetup;
2145c523858385176c33a7456bb84035de78552d22dMarc Blank        public boolean hide;
215751da261722f6b8964c9a4f4b198aeb730addb50Tony Mantler        public boolean isGmailStub;
216f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2175c523858385176c33a7456bb84035de78552d22dMarc Blank        @Override
218f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public String toString() {
219f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            StringBuilder sb = new StringBuilder("Protocol: ");
220f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            sb.append(protocol);
221f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            sb.append(", ");
222f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            sb.append(klass != null ? "Local" : "Remote");
223af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            sb.append(" , Account Type: ");
224af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            sb.append(accountType);
225f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return sb.toString();
226f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
22791237e9dcb0a948f17488b464edabcea0f259d31Makoto Onuki    }
22891237e9dcb0a948f17488b464edabcea0f259d31Makoto Onuki
2292075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu    public static EmailServiceProxy getService(Context context, String protocol) {
230cf3b3ae987e73453f2265673a7ba3a72a1a6b4edMarc Blank        EmailServiceInfo info = null;
231f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Handle the degenerate case here (account might have been deleted)
232cf3b3ae987e73453f2265673a7ba3a72a1a6b4edMarc Blank        if (protocol != null) {
233cf3b3ae987e73453f2265673a7ba3a72a1a6b4edMarc Blank            info = getServiceInfo(context, protocol);
234cf3b3ae987e73453f2265673a7ba3a72a1a6b4edMarc Blank        }
235cf3b3ae987e73453f2265673a7ba3a72a1a6b4edMarc Blank        if (info == null) {
23684e010a463ea5c006d55b7e32268fed40e40262fTony Mantler            LogUtils.w(LogUtils.TAG, "Returning NullService for %s", protocol);
2372075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu            return new EmailServiceProxy(context, NullService.class);
238e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        } else  {
2392075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu            return getServiceFromInfo(context, info);
240e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
241e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
242e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
2432075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu    public static EmailServiceProxy getServiceFromInfo(Context context, EmailServiceInfo info) {
244e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (info.klass != null) {
2452075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu            return new EmailServiceProxy(context, info.klass);
246f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
247e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook            final Intent serviceIntent = getServiceIntent(info);
248e4ac06080ba8b8e57531982ea290354b1a9a4305Paul Westbrook            return new EmailServiceProxy(context, serviceIntent);
249f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
25066a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blank    }
25166a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blank
25281b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank    public static EmailServiceInfo getServiceInfoForAccount(Context context, long accountId) {
25381b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank        String protocol = Account.getProtocol(context, accountId);
25481b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank        return getServiceInfo(context, protocol);
25581b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank    }
25681b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank
2573a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler    public static EmailServiceInfo getServiceInfo(Context context, String protocol) {
2583a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler        return getServiceMap(context).get(protocol);
25966a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blank    }
26066a47b8dac5e97e37c30b928bc5a227d74baada9Marc Blank
2613a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler    public static Collection<EmailServiceInfo> getServiceInfoList(Context context) {
2623a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler        return getServiceMap(context).values();
263c18f8f615f134a2b391d06e2d8d49434a8bb94e5Makoto Onuki    }
264c18f8f615f134a2b391d06e2d8d49434a8bb94e5Makoto Onuki
26570edcf05387df33f4761b766add6b80999e425e9Marc Blank    private static void finishAccountManagerBlocker(AccountManagerFuture<?> future) {
26670edcf05387df33f4761b766add6b80999e425e9Marc Blank        try {
26770edcf05387df33f4761b766add6b80999e425e9Marc Blank            // Note: All of the potential errors are simply logged
26870edcf05387df33f4761b766add6b80999e425e9Marc Blank            // here, as there is nothing to actually do about them.
26970edcf05387df33f4761b766add6b80999e425e9Marc Blank            future.getResult();
27070edcf05387df33f4761b766add6b80999e425e9Marc Blank        } catch (OperationCanceledException e) {
27184e010a463ea5c006d55b7e32268fed40e40262fTony Mantler            LogUtils.w(LogUtils.TAG, e, "finishAccountManagerBlocker");
27270edcf05387df33f4761b766add6b80999e425e9Marc Blank        } catch (AuthenticatorException e) {
27384e010a463ea5c006d55b7e32268fed40e40262fTony Mantler            LogUtils.w(LogUtils.TAG, e, "finishAccountManagerBlocker");
27470edcf05387df33f4761b766add6b80999e425e9Marc Blank        } catch (IOException e) {
27584e010a463ea5c006d55b7e32268fed40e40262fTony Mantler            LogUtils.w(LogUtils.TAG, e, "finishAccountManagerBlocker");
27670edcf05387df33f4761b766add6b80999e425e9Marc Blank        }
27770edcf05387df33f4761b766add6b80999e425e9Marc Blank    }
27870edcf05387df33f4761b766add6b80999e425e9Marc Blank
27902f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu    /**
28002f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     * Add an account to the AccountManager.
28102f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     * @param context Our {@link Context}.
28202f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     * @param account The {@link Account} we're adding.
28302f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     * @param email Whether the user wants to sync email on this account.
28402f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     * @param calendar Whether the user wants to sync calendar on this account.
28502f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     * @param contacts Whether the user wants to sync contacts on this account.
28602f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     * @param callback A callback for when the AccountManager is done.
28702f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     * @return The result of {@link AccountManager#addAccount}.
28802f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu     */
28902f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu    public static AccountManagerFuture<Bundle> setupAccountManagerAccount(final Context context,
29002f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu            final Account account, final boolean email, final boolean calendar,
29102f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu            final boolean contacts, final AccountManagerCallback<Bundle> callback) {
29202f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu        final HostAuth hostAuthRecv =
29302f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu                HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
29420110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee        return setupAccountManagerAccount(context, account, email, calendar, contacts,
29520110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee                hostAuthRecv, callback);
29620110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee    }
29720110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee
29820110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee    /**
29920110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * Add an account to the AccountManager.
30020110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * @param context Our {@link Context}.
30120110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * @param account The {@link Account} we're adding.
30220110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * @param email Whether the user wants to sync email on this account.
30320110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * @param calendar Whether the user wants to sync calendar on this account.
30420110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * @param contacts Whether the user wants to sync contacts on this account.
30520110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * @param hostAuth HostAuth that identifies the protocol and password for this account.
30620110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * @param callback A callback for when the AccountManager is done.
30720110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     * @return The result of {@link AccountManager#addAccount}.
30820110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee     */
30920110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee    public static AccountManagerFuture<Bundle> setupAccountManagerAccount(final Context context,
31020110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee            final Account account, final boolean email, final boolean calendar,
31120110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee            final boolean contacts, final HostAuth hostAuth,
31220110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee            final AccountManagerCallback<Bundle> callback) {
31320110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee        if (hostAuth == null) {
31402f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu            return null;
31502f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu        }
31602f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu        // Set up username/password
31720110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee        final Bundle options = new Bundle(5);
31802f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu        options.putString(EasAuthenticatorService.OPTIONS_USERNAME, account.mEmailAddress);
31920110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee        options.putString(EasAuthenticatorService.OPTIONS_PASSWORD, hostAuth.mPassword);
32002f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu        options.putBoolean(EasAuthenticatorService.OPTIONS_CONTACTS_SYNC_ENABLED, contacts);
32102f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu        options.putBoolean(EasAuthenticatorService.OPTIONS_CALENDAR_SYNC_ENABLED, calendar);
32202f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu        options.putBoolean(EasAuthenticatorService.OPTIONS_EMAIL_SYNC_ENABLED, email);
32320110e628830a906f4c6a80eabb0bb127bf7d4beAnthony Lee        final EmailServiceInfo info = getServiceInfo(context, hostAuth.mProtocol);
32402f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu        return AccountManager.get(context).addAccount(info.accountType, null, null, options, null,
32502f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu                callback, null);
32602f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu    }
32702f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu
328462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu    public static void updateAccountManagerType(Context context,
32948b14fde961ef57103f7a31196254e357987f203Yu Ping Hu            android.accounts.Account amAccount, final Map<String, String> protocolMap) {
33032881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook        final ContentResolver resolver = context.getContentResolver();
33132881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook        final Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION,
33270edcf05387df33f4761b766add6b80999e425e9Marc Blank                AccountColumns.EMAIL_ADDRESS + "=?", new String[] { amAccount.name }, null);
33370edcf05387df33f4761b766add6b80999e425e9Marc Blank        // That's odd, isn't it?
33470edcf05387df33f4761b766add6b80999e425e9Marc Blank        if (c == null) return;
33570edcf05387df33f4761b766add6b80999e425e9Marc Blank        try {
33670edcf05387df33f4761b766add6b80999e425e9Marc Blank            if (c.moveToNext()) {
33770edcf05387df33f4761b766add6b80999e425e9Marc Blank                // Get the EmailProvider Account/HostAuth
33832881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                final Account account = new Account();
33970edcf05387df33f4761b766add6b80999e425e9Marc Blank                account.restore(c);
34032881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                final HostAuth hostAuth =
34170edcf05387df33f4761b766add6b80999e425e9Marc Blank                        HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
34232881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                if (hostAuth == null) {
34332881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                    return;
34432881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                }
34570edcf05387df33f4761b766add6b80999e425e9Marc Blank
346462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                final String newProtocol = protocolMap.get(hostAuth.mProtocol);
347462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                if (newProtocol == null) {
348462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                    // This account doesn't need updating.
349e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    return;
350e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                }
351462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu
35284e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                LogUtils.w(LogUtils.TAG, "Converting %s to %s", amAccount.name, newProtocol);
353e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
35432881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                final ContentValues accountValues = new ContentValues();
35570edcf05387df33f4761b766add6b80999e425e9Marc Blank                int oldFlags = account.mFlags;
35670edcf05387df33f4761b766add6b80999e425e9Marc Blank
35770edcf05387df33f4761b766add6b80999e425e9Marc Blank                // Mark the provider account incomplete so it can't get reconciled away
35870edcf05387df33f4761b766add6b80999e425e9Marc Blank                account.mFlags |= Account.FLAGS_INCOMPLETE;
35970edcf05387df33f4761b766add6b80999e425e9Marc Blank                accountValues.put(AccountColumns.FLAGS, account.mFlags);
36032881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                final Uri accountUri = ContentUris.withAppendedId(Account.CONTENT_URI, account.mId);
36170edcf05387df33f4761b766add6b80999e425e9Marc Blank                resolver.update(accountUri, accountValues, null, null);
36270edcf05387df33f4761b766add6b80999e425e9Marc Blank
36370edcf05387df33f4761b766add6b80999e425e9Marc Blank                // Change the HostAuth to reference the new protocol; this has to be done before
36470edcf05387df33f4761b766add6b80999e425e9Marc Blank                // trying to create the AccountManager account (below)
36532881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                final ContentValues hostValues = new ContentValues();
3663dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                hostValues.put(HostAuthColumns.PROTOCOL, newProtocol);
36770edcf05387df33f4761b766add6b80999e425e9Marc Blank                resolver.update(ContentUris.withAppendedId(HostAuth.CONTENT_URI, hostAuth.mId),
36870edcf05387df33f4761b766add6b80999e425e9Marc Blank                        hostValues, null, null);
36984e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                LogUtils.w(LogUtils.TAG, "Updated HostAuths");
37070edcf05387df33f4761b766add6b80999e425e9Marc Blank
37170edcf05387df33f4761b766add6b80999e425e9Marc Blank                try {
37270edcf05387df33f4761b766add6b80999e425e9Marc Blank                    // Get current settings for the existing AccountManager account
37370edcf05387df33f4761b766add6b80999e425e9Marc Blank                    boolean email = ContentResolver.getSyncAutomatically(amAccount,
37470edcf05387df33f4761b766add6b80999e425e9Marc Blank                            EmailContent.AUTHORITY);
375e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    if (!email) {
376e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        // Try our old provider name
377e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        email = ContentResolver.getSyncAutomatically(amAccount,
378e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                "com.android.email.provider");
379e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    }
38032881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                    final boolean contacts = ContentResolver.getSyncAutomatically(amAccount,
38170edcf05387df33f4761b766add6b80999e425e9Marc Blank                            ContactsContract.AUTHORITY);
38232881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                    final boolean calendar = ContentResolver.getSyncAutomatically(amAccount,
38370edcf05387df33f4761b766add6b80999e425e9Marc Blank                            CalendarContract.AUTHORITY);
38484e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                    LogUtils.w(LogUtils.TAG, "Email: %s, Contacts: %s Calendar: %s",
38584e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                            email, contacts, calendar);
386e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
387e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    // Get sync keys for calendar/contacts
38832881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                    final String amName = amAccount.name;
38932881e8dd375f16e6059452166503f7136f14ae5Paul Westbrook                    final String oldType = amAccount.type;
390e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    ContentProviderClient client = context.getContentResolver()
391e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                            .acquireContentProviderClient(CalendarContract.CONTENT_URI);
392e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    byte[] calendarSyncKey = null;
393e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    try {
394e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        calendarSyncKey = SyncStateContract.Helpers.get(client,
395e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                asCalendarSyncAdapter(SyncState.CONTENT_URI, amName, oldType),
396e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                new android.accounts.Account(amName, oldType));
397e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    } catch (RemoteException e) {
39884e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                        LogUtils.w(LogUtils.TAG, "Get calendar key FAILED");
399e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    } finally {
400e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        client.release();
401e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    }
402e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    client = context.getContentResolver()
403e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                            .acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
404e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    byte[] contactsSyncKey = null;
405e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    try {
406e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        contactsSyncKey = SyncStateContract.Helpers.get(client,
407e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                ContactsContract.SyncState.CONTENT_URI,
408e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                new android.accounts.Account(amName, oldType));
409e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    } catch (RemoteException e) {
41084e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                        LogUtils.w(LogUtils.TAG, "Get contacts key FAILED");
411e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    } finally {
412e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        client.release();
413e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    }
414e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    if (calendarSyncKey != null) {
41584e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                        LogUtils.w(LogUtils.TAG, "Got calendar key: %s",
41684e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                                new String(calendarSyncKey));
417e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    }
418e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    if (contactsSyncKey != null) {
41984e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                        LogUtils.w(LogUtils.TAG, "Got contacts key: %s",
42084e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                                new String(contactsSyncKey));
421e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    }
422e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
423e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    // Set up a new AccountManager account with new type and old settings
42402f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu                    AccountManagerFuture<?> amFuture = setupAccountManagerAccount(context, account,
42502f0b1eee8f5de651ad5f0289146f88fea99bce0Yu Ping Hu                            email, calendar, contacts, null);
426e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    finishAccountManagerBlocker(amFuture);
42784e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                    LogUtils.w(LogUtils.TAG, "Created new AccountManager account");
42870edcf05387df33f4761b766add6b80999e425e9Marc Blank
4293aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                    // TODO: Clean up how we determine the type.
4303aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                    final String accountType = protocolMap.get(hostAuth.mProtocol + "_type");
4313aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                    // Move calendar and contacts data from the old account to the new one.
4323aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                    // We must do this before deleting the old account or the data is lost.
4333aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                    moveCalendarData(context.getContentResolver(), amName, oldType, accountType);
4343aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                    moveContactsData(context.getContentResolver(), amName, oldType, accountType);
4357d6d8c87f86a17cae59af645637458b17e72c716Martin Hibdon
43670edcf05387df33f4761b766add6b80999e425e9Marc Blank                    // Delete the AccountManager account
437e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    amFuture = AccountManager.get(context)
43870edcf05387df33f4761b766add6b80999e425e9Marc Blank                            .removeAccount(amAccount, null, null);
43970edcf05387df33f4761b766add6b80999e425e9Marc Blank                    finishAccountManagerBlocker(amFuture);
44084e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                    LogUtils.w(LogUtils.TAG, "Deleted old AccountManager account");
44170edcf05387df33f4761b766add6b80999e425e9Marc Blank
442e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    // Restore sync keys for contacts/calendar
4433aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon
444462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                    if (accountType != null &&
445462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                            calendarSyncKey != null && calendarSyncKey.length != 0) {
446e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        client = context.getContentResolver()
447e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                .acquireContentProviderClient(CalendarContract.CONTENT_URI);
448e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        try {
449e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                            SyncStateContract.Helpers.set(client,
450e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                    asCalendarSyncAdapter(SyncState.CONTENT_URI, amName,
451462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                                            accountType),
452462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                                    new android.accounts.Account(amName, accountType),
453e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                    calendarSyncKey);
45484e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                            LogUtils.w(LogUtils.TAG, "Set calendar key...");
455e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        } catch (RemoteException e) {
45684e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                            LogUtils.w(LogUtils.TAG, "Set calendar key FAILED");
457e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        } finally {
458e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                            client.release();
459e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        }
460e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    }
461462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                    if (accountType != null &&
462462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                            contactsSyncKey != null && contactsSyncKey.length != 0) {
463e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        client = context.getContentResolver()
464e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                .acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
465e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        try {
466e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                            SyncStateContract.Helpers.set(client,
467e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                    ContactsContract.SyncState.CONTENT_URI,
468462da178b1be608a74000956c53dc8ecb438c689Yu Ping Hu                                    new android.accounts.Account(amName, accountType),
469e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                                    contactsSyncKey);
47084e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                            LogUtils.w(LogUtils.TAG, "Set contacts key...");
471e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        } catch (RemoteException e) {
47284e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                            LogUtils.w(LogUtils.TAG, "Set contacts key FAILED");
473e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        }
474e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    }
475e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
476e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                    // That's all folks!
47784e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                    LogUtils.w(LogUtils.TAG, "Account update completed.");
47870edcf05387df33f4761b766add6b80999e425e9Marc Blank                } finally {
47970edcf05387df33f4761b766add6b80999e425e9Marc Blank                    // Clear the incomplete flag on the provider account
48070edcf05387df33f4761b766add6b80999e425e9Marc Blank                    accountValues.put(AccountColumns.FLAGS, oldFlags);
48170edcf05387df33f4761b766add6b80999e425e9Marc Blank                    resolver.update(accountUri, accountValues, null, null);
48284e010a463ea5c006d55b7e32268fed40e40262fTony Mantler                    LogUtils.w(LogUtils.TAG, "[Incomplete flag cleared]");
48370edcf05387df33f4761b766add6b80999e425e9Marc Blank                }
48470edcf05387df33f4761b766add6b80999e425e9Marc Blank            }
48570edcf05387df33f4761b766add6b80999e425e9Marc Blank        } finally {
48670edcf05387df33f4761b766add6b80999e425e9Marc Blank            c.close();
48770edcf05387df33f4761b766add6b80999e425e9Marc Blank        }
48870edcf05387df33f4761b766add6b80999e425e9Marc Blank    }
48970edcf05387df33f4761b766add6b80999e425e9Marc Blank
4903aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon    private static void moveCalendarData(final ContentResolver resolver, final String name,
4913aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon            final String oldType, final String newType) {
4923aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        final Uri oldCalendars = Calendars.CONTENT_URI.buildUpon()
4933aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
4943aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                .appendQueryParameter(Calendars.ACCOUNT_NAME, name)
4953aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                .appendQueryParameter(Calendars.ACCOUNT_TYPE, oldType)
4963aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                .build();
4973aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon
4983aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        // Update this calendar to have the new account type.
4993aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        final ContentValues values = new ContentValues();
5003aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        values.put(CalendarContract.Calendars.ACCOUNT_TYPE, newType);
5013aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        resolver.update(oldCalendars, values,
5023aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?",
5033aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                new String[] {name, oldType});
5043aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon    }
5053aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon
5063aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon    private static void moveContactsData(final ContentResolver resolver, final String name,
5073aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon            final String oldType, final String newType) {
5083aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        final Uri oldContacts = RawContacts.CONTENT_URI.buildUpon()
5093aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
5103aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                .appendQueryParameter(RawContacts.ACCOUNT_NAME, name)
5113aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                .appendQueryParameter(RawContacts.ACCOUNT_TYPE, oldType)
5123aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon                .build();
5133aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon
5143aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        // Update this calendar to have the new account type.
5153aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        final ContentValues values = new ContentValues();
5163aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        values.put(CalendarContract.Calendars.ACCOUNT_TYPE, newType);
5173aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon        resolver.update(oldContacts, values, null, null);
5183aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon    }
5193aad3fcb8bb916c989fe67cfbf527eb0df6fc6d1Martin Hibdon
5203a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler    private static final Configuration sOldConfiguration = new Configuration();
5213a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler    private static Map<String, EmailServiceInfo> sServiceMap = null;
5223a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler    private static final Object sServiceMapLock = new Object();
5233a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler
524c18f8f615f134a2b391d06e2d8d49434a8bb94e5Makoto Onuki    /**
525f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Parse services.xml file to find our available email services
52691237e9dcb0a948f17488b464edabcea0f259d31Makoto Onuki     */
5273a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler    private static Map<String, EmailServiceInfo> getServiceMap(final Context context) {
5283a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler        synchronized (sServiceMapLock) {
5293a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            /**
5303a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler             * We cache localized strings here, so make sure to regenerate the service map if
5313a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler             * the locale changes
5323a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler             */
5333a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            if (sServiceMap == null) {
5343a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                sOldConfiguration.setTo(context.getResources().getConfiguration());
5353a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            }
536b5eed31d1bdfcb8e8b316c9c49331295718001bdYu Ping Hu
5373a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            final int delta =
5383a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                    sOldConfiguration.updateFrom(context.getResources().getConfiguration());
539b5eed31d1bdfcb8e8b316c9c49331295718001bdYu Ping Hu
5403a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            if (sServiceMap != null
5413a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                    && !Configuration.needNewResources(delta, ActivityInfo.CONFIG_LOCALE)) {
5423a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                return sServiceMap;
5433a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            }
5443a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler
5453a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            final ImmutableMap.Builder<String, EmailServiceInfo> builder = ImmutableMap.builder();
5460397e7fb233bbf76f7ea30ab3e447f74d7c6bcceAnthony Lee            if (!context.getResources().getBoolean(R.bool.enable_services)) {
5470397e7fb233bbf76f7ea30ab3e447f74d7c6bcceAnthony Lee                // Return an empty map if services have been disabled because this is the Email
5480397e7fb233bbf76f7ea30ab3e447f74d7c6bcceAnthony Lee                // Tombstone app.
5490397e7fb233bbf76f7ea30ab3e447f74d7c6bcceAnthony Lee                sServiceMap = builder.build();
5500397e7fb233bbf76f7ea30ab3e447f74d7c6bcceAnthony Lee                return sServiceMap;
5510397e7fb233bbf76f7ea30ab3e447f74d7c6bcceAnthony Lee            }
5523a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler
5533a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            try {
5543a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                final Resources res = context.getResources();
5553a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                final XmlResourceParser xml = res.getXml(R.xml.services);
5563a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                int xmlEventType;
5573a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                // walk through senders.xml file.
5583a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
5593a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                    if (xmlEventType == XmlResourceParser.START_TAG &&
5603a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            "emailservice".equals(xml.getName())) {
5613a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        final EmailServiceInfo info = new EmailServiceInfo();
5623a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        final TypedArray ta =
5633a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                res.obtainAttributes(xml, R.styleable.EmailServiceInfo);
5643a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.protocol = ta.getString(R.styleable.EmailServiceInfo_protocol);
5653a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.accountType = ta.getString(R.styleable.EmailServiceInfo_accountType);
5663a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.name = ta.getString(R.styleable.EmailServiceInfo_name);
5673a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.hide = ta.getBoolean(R.styleable.EmailServiceInfo_hide, false);
5683a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        final String klass =
5693a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                ta.getString(R.styleable.EmailServiceInfo_serviceClass);
5703a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.intentAction = ta.getString(R.styleable.EmailServiceInfo_intent);
5713a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.intentPackage =
5723a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                ta.getString(R.styleable.EmailServiceInfo_intentPackage);
5733a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.defaultSsl =
5743a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                ta.getBoolean(R.styleable.EmailServiceInfo_defaultSsl, false);
5753a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.port = ta.getInteger(R.styleable.EmailServiceInfo_port, 0);
5763a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.portSsl = ta.getInteger(R.styleable.EmailServiceInfo_portSsl, 0);
5773a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.offerTls = ta.getBoolean(R.styleable.EmailServiceInfo_offerTls, false);
5783a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.offerCerts =
5793a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                ta.getBoolean(R.styleable.EmailServiceInfo_offerCerts, false);
5809195a1202483053606f43e871915a5405e955306Martin Hibdon                        info.offerOAuth =
5819195a1202483053606f43e871915a5405e955306Martin Hibdon                                ta.getBoolean(R.styleable.EmailServiceInfo_offerOAuth, false);
5823a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.offerLocalDeletes =
5833a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getBoolean(R.styleable.EmailServiceInfo_offerLocalDeletes, false);
5843a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.defaultLocalDeletes =
5853a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getInteger(R.styleable.EmailServiceInfo_defaultLocalDeletes,
5863a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                    Account.DELETE_POLICY_ON_DELETE);
5873a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.offerPrefix =
5883a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getBoolean(R.styleable.EmailServiceInfo_offerPrefix, false);
5893a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.usesSmtp = ta.getBoolean(R.styleable.EmailServiceInfo_usesSmtp, false);
5903a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.usesAutodiscover =
5913a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getBoolean(R.styleable.EmailServiceInfo_usesAutodiscover, false);
5923a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.offerLookback =
5933a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getBoolean(R.styleable.EmailServiceInfo_offerLookback, false);
5943a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.defaultLookback =
5953a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getInteger(R.styleable.EmailServiceInfo_defaultLookback,
5963a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                    SyncWindow.SYNC_WINDOW_3_DAYS);
5973a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.syncChanges =
5983a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getBoolean(R.styleable.EmailServiceInfo_syncChanges, false);
5993a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.syncContacts =
6003a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getBoolean(R.styleable.EmailServiceInfo_syncContacts, false);
6013a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.syncCalendar =
6023a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getBoolean(R.styleable.EmailServiceInfo_syncCalendar, false);
6033a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.offerAttachmentPreload =
6043a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getBoolean(R.styleable.EmailServiceInfo_offerAttachmentPreload,
6053a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                    false);
6063a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.syncIntervalStrings =
6073a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getTextArray(R.styleable.EmailServiceInfo_syncIntervalStrings);
6083a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.syncIntervals =
6093a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getTextArray(R.styleable.EmailServiceInfo_syncIntervals);
6103a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.defaultSyncInterval =
6113a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            ta.getInteger(R.styleable.EmailServiceInfo_defaultSyncInterval, 15);
6123a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.inferPrefix = ta.getString(R.styleable.EmailServiceInfo_inferPrefix);
6133a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.offerLoadMore =
6143a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                ta.getBoolean(R.styleable.EmailServiceInfo_offerLoadMore, false);
615ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert                        info.offerMoveTo =
616ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert                                ta.getBoolean(R.styleable.EmailServiceInfo_offerMoveTo, false);
6173a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        info.requiresSetup =
6183a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                ta.getBoolean(R.styleable.EmailServiceInfo_requiresSetup, false);
619751da261722f6b8964c9a4f4b198aeb730addb50Tony Mantler                        info.isGmailStub =
620751da261722f6b8964c9a4f4b198aeb730addb50Tony Mantler                                ta.getBoolean(R.styleable.EmailServiceInfo_isGmailStub, false);
6213a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler
6223a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        // Must have either "class" (local) or "intent" (remote)
6233a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        if (klass != null) {
6243a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            try {
6253a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                // noinspection unchecked
6263a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                info.klass = (Class<? extends Service>) Class.forName(klass);
6273a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            } catch (ClassNotFoundException e) {
6283a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                throw new IllegalStateException(
6293a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                        "Class not found in service descriptor: " + klass);
6303a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            }
6313a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        }
632751da261722f6b8964c9a4f4b198aeb730addb50Tony Mantler                        if (info.klass == null &&
633751da261722f6b8964c9a4f4b198aeb730addb50Tony Mantler                                info.intentAction == null &&
634751da261722f6b8964c9a4f4b198aeb730addb50Tony Mantler                                !info.isGmailStub) {
635f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            throw new IllegalStateException(
6363a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                    "No class or intent action specified in service descriptor");
637f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
6383a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        if (info.klass != null && info.intentAction != null) {
6393a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                            throw new IllegalStateException(
6403a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                                    "Both class and intent action specified in service descriptor");
6413a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        }
6423a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                        builder.put(info.protocol, info);
643f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
644f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
6453a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            } catch (XmlPullParserException e) {
6463a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                // ignore
6473a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            } catch (IOException e) {
6483a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler                // ignore
649f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
6503a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            sServiceMap = builder.build();
6513a8c86b18265eb3786bf32b5c043b5942ddb4e45Tony Mantler            return sServiceMap;
65203cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
653f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
65453b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler
65553b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler    /**
65653b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler     * Resolves a service name into a protocol name, or null if ambiguous
65753b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler     * @param context for loading service map
65853b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler     * @param accountType sync adapter service name
65953b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler     * @return protocol name or null
66053b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler     */
6614c865846856cd7530fb51e244a9971a3d5e1fcd8Tony Mantler    public static @Nullable String getProtocolFromAccountType(final Context context,
66253b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler            final String accountType) {
6634c865846856cd7530fb51e244a9971a3d5e1fcd8Tony Mantler        if (TextUtils.isEmpty(accountType)) {
6644c865846856cd7530fb51e244a9971a3d5e1fcd8Tony Mantler            return null;
6654c865846856cd7530fb51e244a9971a3d5e1fcd8Tony Mantler        }
66653b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler        final Map <String, EmailServiceInfo> serviceInfoMap = getServiceMap(context);
66753b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler        String protocol = null;
66853b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler        for (final EmailServiceInfo info : serviceInfoMap.values()) {
66953b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler            if (TextUtils.equals(accountType, info.accountType)) {
67053b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler                if (!TextUtils.isEmpty(protocol) && !TextUtils.equals(protocol, info.protocol)) {
67153b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler                    // More than one protocol matches
67253b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler                    return null;
67353b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler                }
67453b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler                protocol = info.protocol;
67553b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler            }
67653b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler        }
67753b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler        return protocol;
67853b64eee54cdb74dc325089b8362a6ff4811759eTony Mantler    }
67903cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
680e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static Uri asCalendarSyncAdapter(Uri uri, String account, String accountType) {
681e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return uri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
682e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
683e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
684e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
685e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
686f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
687f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * A no-op service that can be returned for non-existent/null protocols
688f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
689f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    class NullService implements IEmailService {
690f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
691f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public IBinder asBinder() {
692f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return null;
69303cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
69403cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
695f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
6961d8fd9c05449863ac6a151a6db87c267c4f55ac2Martin Hibdon        public Bundle validate(HostAuthCompat hostauth) throws RemoteException {
697f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return null;
69803cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
69903cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
700f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
70170cb2878d72ce1c1775ac0426ff82698a5e59377Anthony Lee        public void loadAttachment(final IEmailServiceCallback cb, final long accountId,
70270cb2878d72ce1c1775ac0426ff82698a5e59377Anthony Lee                final long attachmentId, final boolean background) throws RemoteException {
70303cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
70403cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
705f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
706fb2a3a2f77f954265beaa92b87774006e2d7c185Martin Hibdon        public void updateFolderList(long accountId) throws RemoteException {}
70703cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
708f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
709fb2a3a2f77f954265beaa92b87774006e2d7c185Martin Hibdon        public void setLogging(int flags) throws RemoteException {
71003cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
71103cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
712f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
713f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public Bundle autoDiscover(String userName, String password) throws RemoteException {
714f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return null;
71503cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
71603cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
717f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
718f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public void sendMeetingResponse(long messageId, int response) throws RemoteException {
71903cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
72003cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
721f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
7227afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon        public void deleteExternalAccountPIMData(final String emailAddress) throws RemoteException {
723c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon        }
724c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon
725c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon        @Override
726f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public int searchMessages(long accountId, SearchParams params, long destMailboxId)
727f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                throws RemoteException {
72803cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank            return 0;
72903cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
73003cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
731f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        @Override
732f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        public void sendMail(long accountId) throws RemoteException {
73303cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        }
734701134953e4591bf2cca020838558a97eab570a1Yu Ping Hu
735701134953e4591bf2cca020838558a97eab570a1Yu Ping Hu        @Override
736701134953e4591bf2cca020838558a97eab570a1Yu Ping Hu        public void pushModify(long accountId) throws RemoteException {
737701134953e4591bf2cca020838558a97eab570a1Yu Ping Hu        }
7388103f960d0c0469d67652afd616e9924687aa139Yu Ping Hu
7398103f960d0c0469d67652afd616e9924687aa139Yu Ping Hu        @Override
740fb2a3a2f77f954265beaa92b87774006e2d7c185Martin Hibdon        public int sync(final long accountId, final Bundle syncExtras) {
741fb2a3a2f77f954265beaa92b87774006e2d7c185Martin Hibdon            return EmailServiceStatus.SUCCESS;
7428103f960d0c0469d67652afd616e9924687aa139Yu Ping Hu        }
7438103f960d0c0469d67652afd616e9924687aa139Yu Ping Hu
7447afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon        public int getApiVersion() {
7457afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon            return EmailServiceVersion.CURRENT;
7467afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon        }
7479fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki    }
74874dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdon
749ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee    public static void setComponentStatus(final Context context, Class<?> clazz, boolean enabled) {
75074dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdon        final ComponentName c = new ComponentName(context, clazz.getName());
75174dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdon        context.getPackageManager().setComponentEnabledSetting(c,
75274dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdon                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
75374dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdon                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
75474dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdon                PackageManager.DONT_KILL_APP);
75574dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdon    }
75674dc106ae9ff3d3a92425b68ea28ee2a0b258e57Martin Hibdon
757ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee    /**
758ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee     * This is a helper function that enables the proper Exchange component and disables
759ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee     * the other Exchange component ensuring that only one is enabled at a time.
760ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee     */
761ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee    public static void enableExchangeComponent(final Context context) {
762ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee        if (VendorPolicyLoader.getInstance(context).useAlternateExchangeStrings()) {
763ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee            LogUtils.d(LogUtils.TAG, "Enabling alternate EAS authenticator");
764ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee            setComponentStatus(context, EasAuthenticatorServiceAlternate.class, true);
765ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee            setComponentStatus(context, EasAuthenticatorService.class, false);
766ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee        } else {
767ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee            LogUtils.d(LogUtils.TAG, "Enabling EAS authenticator");
768ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee            setComponentStatus(context, EasAuthenticatorService.class, true);
769ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee            setComponentStatus(context,
770ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee                    EasAuthenticatorServiceAlternate.class, false);
771ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee        }
772ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee    }
773ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee
774ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee    public static void disableExchangeComponents(final Context context) {
775ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee        LogUtils.d(LogUtils.TAG, "Disabling EAS authenticators");
776ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee        setComponentStatus(context, EasAuthenticatorServiceAlternate.class, false);
777ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee        setComponentStatus(context, EasAuthenticatorService.class, false);
778ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee    }
779ebe49cfeaf0328aa95f5cb82b731f08d7bae7b09Anthony Lee
7809fe51f632965f5d085ae45a1089c7c97dcec8881Makoto Onuki}
781