Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / signin / SigninManager.java
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.signin;
6
7 import android.accounts.Account;
8 import android.app.Activity;
9 import android.app.AlertDialog;
10 import android.app.ProgressDialog;
11 import android.content.Context;
12 import android.content.DialogInterface;
13 import android.os.Handler;
14 import android.util.Log;
15
16 import org.chromium.base.ActivityState;
17 import org.chromium.base.ApplicationStatus;
18 import org.chromium.base.CalledByNative;
19 import org.chromium.base.ObserverList;
20 import org.chromium.base.ThreadUtils;
21 import org.chromium.chrome.R;
22 import org.chromium.chrome.browser.invalidation.InvalidationController;
23 import org.chromium.chrome.browser.sync.ProfileSyncService;
24 import org.chromium.sync.internal_api.pub.base.ModelType;
25 import org.chromium.sync.notifier.SyncStatusHelper;
26 import org.chromium.sync.signin.ChromeSigninController;
27
28 import java.util.HashSet;
29
30 /**
31  * Android wrapper of the SigninManager which provides access from the Java layer.
32  * <p/>
33  * This class handles common paths during the sign-in and sign-out flows.
34  * <p/>
35  * Only usable from the UI thread as the native SigninManager requires its access to be in the
36  * UI thread.
37  * <p/>
38  * See chrome/browser/signin/signin_manager_android.h for more details.
39  */
40 public class SigninManager {
41
42     private static final String TAG = "SigninManager";
43
44     private static SigninManager sSigninManager;
45
46     private final Context mContext;
47     private final long mNativeSigninManagerAndroid;
48
49     /** Tracks whether the First Run check has been completed.
50      *
51      * A new sign-in can not be started while this is pending, to prevent the
52      * pending check from eventually starting a 2nd sign-in.
53      */
54     private boolean mFirstRunCheckIsPending = true;
55     private final ObserverList<SignInAllowedObserver> mSignInAllowedObservers =
56             new ObserverList<SignInAllowedObserver>();
57
58     private Activity mSignInActivity;
59     private Account mSignInAccount;
60     private Observer mSignInObserver;
61     private boolean mPassive = false;
62
63     private ProgressDialog mSignOutProgressDialog;
64     private Runnable mSignOutCallback;
65
66     private AlertDialog mPolicyConfirmationDialog;
67
68     /**
69      * SignInAllowedObservers will be notified once signing-in becomes allowed or disallowed.
70      */
71     public static interface SignInAllowedObserver {
72         /**
73          * Invoked once all startup checks are done and signing-in becomes allowed, or disallowed.
74          */
75         public void onSignInAllowedChanged();
76     }
77
78     /**
79      * The Observer of startSignIn() will be notified when sign-in completes.
80      */
81     public static interface Observer {
82         /**
83          * Invoked after sign-in completed successfully.
84          */
85         public void onSigninComplete();
86
87         /**
88          * Invoked when the sign-in process was cancelled by the user.
89          *
90          * The user should have the option of going back and starting the process again,
91          * if possible.
92          */
93         public void onSigninCancelled();
94     }
95
96     /**
97      * A helper method for retrieving the application-wide SigninManager.
98      * <p/>
99      * Can only be accessed on the main thread.
100      *
101      * @param context the ApplicationContext is retrieved from the context used as an argument.
102      * @return a singleton instance of the SigninManager.
103      */
104     public static SigninManager get(Context context) {
105         ThreadUtils.assertOnUiThread();
106         if (sSigninManager == null) {
107             sSigninManager = new SigninManager(context);
108         }
109         return sSigninManager;
110     }
111
112     private SigninManager(Context context) {
113         ThreadUtils.assertOnUiThread();
114         mContext = context.getApplicationContext();
115         mNativeSigninManagerAndroid = nativeInit();
116     }
117
118     /**
119      * Notifies the SigninManager that the First Run check has completed.
120      *
121      * The user will be allowed to sign-in once this is signaled.
122      */
123     public void onFirstRunCheckDone() {
124         mFirstRunCheckIsPending = false;
125
126         if (isSignInAllowed()) {
127             notifySignInAllowedChanged();
128         }
129     }
130
131     /**
132      * Returns true if signin can be started now.
133      */
134     public boolean isSignInAllowed() {
135         return !mFirstRunCheckIsPending &&
136                 mSignInAccount == null &&
137                 ChromeSigninController.get(mContext).getSignedInUser() == null;
138     }
139
140     public void addSignInAllowedObserver(SignInAllowedObserver observer) {
141         mSignInAllowedObservers.addObserver(observer);
142     }
143
144     public void removeSignInAllowedObserver(SignInAllowedObserver observer) {
145         mSignInAllowedObservers.removeObserver(observer);
146     }
147
148     private void notifySignInAllowedChanged() {
149         new Handler().post(new Runnable() {
150             @Override
151             public void run() {
152                 for (SignInAllowedObserver observer : mSignInAllowedObservers) {
153                     observer.onSignInAllowedChanged();
154                 }
155             }
156         });
157     }
158
159     /**
160      * Starts the sign-in flow, and executes the callback when ready to proceed.
161      * <p/>
162      * This method checks with the native side whether the account has management enabled, and may
163      * present a dialog to the user to confirm sign-in. The callback is invoked once these processes
164      * and the common sign-in initialization complete.
165      *
166      * @param activity The context to use for the operation.
167      * @param account The account to sign in to.
168      * @param passive If passive is true then this operation should not interact with the user.
169      * @param observer The Observer to notify when the sign-in process is finished.
170      */
171     public void startSignIn(
172             Activity activity, final Account account, boolean passive, final Observer observer) {
173         assert mSignInActivity == null;
174         assert mSignInAccount == null;
175         assert mSignInObserver == null;
176
177         if (mFirstRunCheckIsPending) {
178             Log.w(TAG, "Ignoring sign-in request until the First Run check completes.");
179             return;
180         }
181
182         mSignInActivity = activity;
183         mSignInAccount = account;
184         mSignInObserver = observer;
185         mPassive = passive;
186
187         notifySignInAllowedChanged();
188
189         if (!nativeShouldLoadPolicyForUser(account.name)) {
190             // Proceed with the sign-in flow without checking for policy if it can be determined
191             // that this account can't have management enabled based on the username.
192             doSignIn();
193             return;
194         }
195
196         Log.d(TAG, "Checking if account has policy management enabled");
197         // This will call back to onPolicyCheckedBeforeSignIn.
198         nativeCheckPolicyBeforeSignIn(mNativeSigninManagerAndroid, account.name);
199     }
200
201     @CalledByNative
202     private void onPolicyCheckedBeforeSignIn(String managementDomain) {
203         if (managementDomain == null) {
204             Log.d(TAG, "Account doesn't have policy");
205             doSignIn();
206             return;
207         }
208
209         if (ApplicationStatus.getStateForActivity(mSignInActivity) == ActivityState.DESTROYED) {
210             // The activity is no longer running, cancel sign in.
211             cancelSignIn();
212             return;
213         }
214
215         if (mPassive) {
216             // If this is a passive interaction (e.g. auto signin) then don't show the confirmation
217             // dialog.
218             nativeFetchPolicyBeforeSignIn(mNativeSigninManagerAndroid);
219             return;
220         }
221
222         Log.d(TAG, "Account has policy management");
223         AlertDialog.Builder builder = new AlertDialog.Builder(mSignInActivity);
224         builder.setTitle(R.string.policy_dialog_title);
225         builder.setMessage(mContext.getResources().getString(R.string.policy_dialog_message,
226                                                              managementDomain));
227         builder.setPositiveButton(
228                 R.string.policy_dialog_proceed,
229                 new DialogInterface.OnClickListener() {
230                     @Override
231                     public void onClick(DialogInterface dialog, int id) {
232                         Log.d(TAG, "Accepted policy management, proceeding with sign-in");
233                         // This will call back to onPolicyFetchedBeforeSignIn.
234                         nativeFetchPolicyBeforeSignIn(mNativeSigninManagerAndroid);
235                         mPolicyConfirmationDialog = null;
236                     }
237                 });
238         builder.setNegativeButton(
239                 R.string.policy_dialog_cancel,
240                 new DialogInterface.OnClickListener() {
241                     @Override
242                     public void onClick(DialogInterface dialog, int id) {
243                         Log.d(TAG, "Cancelled sign-in");
244                         cancelSignIn();
245                         mPolicyConfirmationDialog = null;
246                     }
247                 });
248         mPolicyConfirmationDialog = builder.create();
249         mPolicyConfirmationDialog.setOnDismissListener(
250                 new DialogInterface.OnDismissListener() {
251                     @Override
252                     public void onDismiss(DialogInterface dialog) {
253                         if (mPolicyConfirmationDialog != null) {
254                             Log.d(TAG, "Policy dialog dismissed, cancelling sign-in.");
255                             cancelSignIn();
256                             mPolicyConfirmationDialog = null;
257                         }
258                     }
259                 });
260         mPolicyConfirmationDialog.show();
261     }
262
263     @CalledByNative
264     private void onPolicyFetchedBeforeSignIn() {
265         // Policy has been fetched for the user and is being enforced; features like sync may now
266         // be disabled by policy, and the rest of the sign-in flow can be resumed.
267         doSignIn();
268     }
269
270     private void doSignIn() {
271         Log.d(TAG, "Committing the sign-in process now");
272         assert mSignInAccount != null;
273
274         // Cache the signed-in account name.
275         ChromeSigninController.get(mContext).setSignedInAccountName(mSignInAccount.name);
276
277         // Tell the native side that sign-in has completed.
278         nativeOnSignInCompleted(mNativeSigninManagerAndroid, mSignInAccount.name);
279
280         // Register for invalidations.
281         InvalidationController invalidationController = InvalidationController.get(mContext);
282         invalidationController.setRegisteredTypes(mSignInAccount, true, new HashSet<ModelType>());
283
284         // Sign-in to sync.
285         ProfileSyncService profileSyncService = ProfileSyncService.get(mContext);
286         if (SyncStatusHelper.get(mContext).isSyncEnabled(mSignInAccount) &&
287                 !profileSyncService.hasSyncSetupCompleted()) {
288             profileSyncService.setSetupInProgress(true);
289             profileSyncService.syncSignIn();
290         }
291
292         if (mSignInObserver != null)
293             mSignInObserver.onSigninComplete();
294
295         // All done, cleanup.
296         Log.d(TAG, "Signin done");
297         mSignInActivity = null;
298         mSignInAccount = null;
299         mSignInObserver = null;
300         notifySignInAllowedChanged();
301     }
302
303     /**
304      * Signs out of Chrome.
305      * <p/>
306      * This method clears the signed-in username, stops sync and sends out a
307      * sign-out notification on the native side.
308      *
309      * @param activity If not null then a progress dialog is shown over the activity until signout
310      * completes, in case the account had management enabled. The activity must be valid until the
311      * callback is invoked.
312      * @param callback Will be invoked after signout completes, if not null.
313      */
314     public void signOut(Activity activity, Runnable callback) {
315         mSignOutCallback = callback;
316
317         boolean wipeData = getManagementDomain() != null;
318         Log.d(TAG, "Signing out, wipe data? " + wipeData);
319
320         ChromeSigninController.get(mContext).clearSignedInUser();
321         ProfileSyncService.get(mContext).signOut();
322         nativeSignOut(mNativeSigninManagerAndroid);
323
324         if (wipeData) {
325             wipeProfileData(activity);
326         } else {
327             onSignOutDone();
328         }
329     }
330
331     /**
332      * Returns the management domain if the signed in account is managed, otherwise returns null.
333      */
334     public String getManagementDomain() {
335         return nativeGetManagementDomain(mNativeSigninManagerAndroid);
336     }
337
338     public void logInSignedInUser() {
339         nativeLogInSignedInUser(mNativeSigninManagerAndroid);
340     }
341
342     public void clearLastSignedInUser() {
343         nativeClearLastSignedInUser(mNativeSigninManagerAndroid);
344     }
345
346     private void cancelSignIn() {
347         if (mSignInObserver != null)
348             mSignInObserver.onSigninCancelled();
349         mSignInActivity = null;
350         mSignInObserver = null;
351         mSignInAccount = null;
352         notifySignInAllowedChanged();
353     }
354
355     private void wipeProfileData(Activity activity) {
356         if (activity != null) {
357             // We don't have progress update, so this takes an indeterminate amount of time.
358             boolean indeterminate = true;
359             // This dialog is not cancelable by the user.
360             boolean cancelable = false;
361             mSignOutProgressDialog = ProgressDialog.show(
362                 activity,
363                 activity.getString(R.string.wiping_profile_data_title),
364                 activity.getString(R.string.wiping_profile_data_message),
365                 indeterminate, cancelable);
366         }
367         // This will call back to onProfileDataWiped().
368         nativeWipeProfileData(mNativeSigninManagerAndroid);
369     }
370
371     @CalledByNative
372     private void onProfileDataWiped() {
373         if (mSignOutProgressDialog != null && mSignOutProgressDialog.isShowing())
374             mSignOutProgressDialog.dismiss();
375         onSignOutDone();
376     }
377
378     private void onSignOutDone() {
379         if (mSignOutCallback != null) {
380             new Handler().post(mSignOutCallback);
381             mSignOutCallback = null;
382         }
383     }
384
385     /**
386      * @return True if the new profile management is enabled.
387      */
388     public static boolean isNewProfileManagementEnabled() {
389         return nativeIsNewProfileManagementEnabled();
390     }
391
392     // Native methods.
393     private native long nativeInit();
394     private native boolean nativeShouldLoadPolicyForUser(String username);
395     private native void nativeCheckPolicyBeforeSignIn(
396             long nativeSigninManagerAndroid, String username);
397     private native void nativeFetchPolicyBeforeSignIn(long nativeSigninManagerAndroid);
398     private native void nativeOnSignInCompleted(long nativeSigninManagerAndroid, String username);
399     private native void nativeSignOut(long nativeSigninManagerAndroid);
400     private native String nativeGetManagementDomain(long nativeSigninManagerAndroid);
401     private native void nativeWipeProfileData(long nativeSigninManagerAndroid);
402     private native void nativeClearLastSignedInUser(long nativeSigninManagerAndroid);
403     private native void nativeLogInSignedInUser(long nativeSigninManagerAndroid);
404     private static native boolean nativeIsNewProfileManagementEnabled();
405 }