1 // Copyright 2015 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.
5 package org
.chromium
.chromoting
;
7 import android
.app
.Activity
;
8 import android
.app
.AlertDialog
;
9 import android
.content
.DialogInterface
;
10 import android
.content
.Intent
;
11 import android
.content
.SharedPreferences
;
12 import android
.os
.Build
;
13 import android
.view
.KeyEvent
;
14 import android
.view
.View
;
15 import android
.widget
.CheckBox
;
16 import android
.widget
.TextView
;
17 import android
.widget
.Toast
;
19 import org
.chromium
.chromoting
.jni
.JniInterface
;
22 * This class performs the user-interaction needed to authenticate the session connection. This
23 * includes showing the PIN prompt and requesting tokens for third-party authentication.
25 public class SessionAuthenticator
{
27 * Application context used for getting user preferences, displaying UI, and fetching
30 private Chromoting mApplicationContext
;
32 /** Provides the tokenUrlPatterns for this host during fetchThirdPartyTokens(). */
33 private HostInfo mHost
;
35 /** Object for fetching OAuth2 access tokens from third party authorization servers. */
36 private ThirdPartyTokenFetcher mTokenFetcher
;
38 public SessionAuthenticator(Chromoting context
, HostInfo host
) {
39 mApplicationContext
= context
;
43 public void displayAuthenticationPrompt(boolean pairingSupported
) {
44 AlertDialog
.Builder pinPrompt
= new AlertDialog
.Builder(mApplicationContext
);
45 pinPrompt
.setTitle(mApplicationContext
.getString(R
.string
.title_authenticate
));
46 pinPrompt
.setMessage(mApplicationContext
.getString(R
.string
.pin_message_android
));
47 pinPrompt
.setIcon(android
.R
.drawable
.ic_lock_lock
);
50 mApplicationContext
.getLayoutInflater().inflate(R
.layout
.pin_dialog
, null);
51 pinPrompt
.setView(pinEntry
);
53 final TextView pinTextView
= (TextView
) pinEntry
.findViewById(R
.id
.pin_dialog_text
);
54 final CheckBox pinCheckBox
= (CheckBox
) pinEntry
.findViewById(R
.id
.pin_dialog_check
);
56 if (!pairingSupported
) {
57 pinCheckBox
.setChecked(false);
58 pinCheckBox
.setVisibility(View
.GONE
);
61 pinPrompt
.setPositiveButton(
62 R
.string
.connect_button
, new DialogInterface
.OnClickListener() {
64 public void onClick(DialogInterface dialog
, int which
) {
65 if (JniInterface
.isConnected()) {
66 JniInterface
.handleAuthenticationResponse(
67 String
.valueOf(pinTextView
.getText()),
68 pinCheckBox
.isChecked(), Build
.MODEL
);
71 mApplicationContext
.getString(R
.string
.error_network_error
);
72 Toast
.makeText(mApplicationContext
, message
, Toast
.LENGTH_LONG
).show();
77 pinPrompt
.setNegativeButton(
78 R
.string
.cancel
, new DialogInterface
.OnClickListener() {
80 public void onClick(DialogInterface dialog
, int which
) {
81 JniInterface
.disconnectFromHost();
85 final AlertDialog pinDialog
= pinPrompt
.create();
87 pinTextView
.setOnEditorActionListener(
88 new TextView
.OnEditorActionListener() {
90 public boolean onEditorAction(TextView v
, int actionId
, KeyEvent event
) {
91 // The user pressed enter on the keypad (equivalent to the connect button).
92 pinDialog
.getButton(AlertDialog
.BUTTON_POSITIVE
).performClick();
98 pinDialog
.setOnCancelListener(
99 new DialogInterface
.OnCancelListener() {
101 public void onCancel(DialogInterface dialog
) {
102 // The user backed out of the dialog (equivalent to the cancel button).
103 pinDialog
.getButton(AlertDialog
.BUTTON_NEGATIVE
).performClick();
110 /** Saves newly-received pairing credentials to permanent storage. */
111 public void commitPairingCredentials(String host
, String id
, String secret
) {
112 // Empty |id| indicates that pairing needs to be removed.
114 mApplicationContext
.getPreferences(Activity
.MODE_PRIVATE
).edit()
115 .remove(host
+ "_id")
116 .remove(host
+ "_secret")
119 mApplicationContext
.getPreferences(Activity
.MODE_PRIVATE
).edit()
120 .putString(host
+ "_id", id
)
121 .putString(host
+ "_secret", secret
)
126 public void fetchThirdPartyToken(String tokenUrl
, String clientId
, String scope
) {
127 assert mTokenFetcher
== null;
129 ThirdPartyTokenFetcher
.Callback callback
= new ThirdPartyTokenFetcher
.Callback() {
131 public void onTokenFetched(String code
, String accessToken
) {
132 // The native client sends the OAuth authorization code to the host as the token so
133 // that the host can obtain the shared secret from the third party authorization
137 // The native client uses the OAuth access token as the shared secret to
138 // authenticate itself with the host using spake.
139 String sharedSecret
= accessToken
;
141 JniInterface
.onThirdPartyTokenFetched(token
, sharedSecret
);
144 mTokenFetcher
= new ThirdPartyTokenFetcher(mApplicationContext
, mHost
.getTokenUrlPatterns(),
146 mTokenFetcher
.fetchToken(tokenUrl
, clientId
, scope
);
149 public void onNewIntent(Intent intent
) {
150 if (mTokenFetcher
!= null) {
151 if (mTokenFetcher
.handleTokenFetched(intent
)) {
152 mTokenFetcher
= null;
157 /** Returns the pairing ID for the given host, or an empty string if not set. */
158 public String
getPairingId(String hostId
) {
159 SharedPreferences prefs
= mApplicationContext
.getPreferences(Activity
.MODE_PRIVATE
);
160 return prefs
.getString(hostId
+ "_id", "");
163 /** Returns the pairing secret for the given host, or an empty string if not set. */
164 public String
getPairingSecret(String hostId
) {
165 SharedPreferences prefs
= mApplicationContext
.getPreferences(Activity
.MODE_PRIVATE
);
166 return prefs
.getString(hostId
+ "_secret", "");