Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.google.android.gms.R;

import org.json.JSONArray;
import org.microg.gms.accountaction.AccountNotificationKt;
import org.microg.gms.auth.AuthConstants;
import org.microg.gms.auth.AuthManager;
import org.microg.gms.auth.AuthRequest;
Expand Down Expand Up @@ -85,10 +86,12 @@ public class LoginActivity extends AssistantActivity {
public static final String EXTRA_TMPL = "tmpl";
public static final String EXTRA_EMAIL = "email";
public static final String EXTRA_TOKEN = "masterToken";
public static final String EXTRA_RE_AUTH_ACCOUNT = "re_auth_account";
public static final int STATUS_BAR_DISABLE_BACK = 0x00400000;

private static final String TAG = "GmsAuthLoginBrowser";
private static final String EMBEDDED_SETUP_URL = "https://accounts.google.com/EmbeddedSetup";
private static final String EMBEDDED_RE_AUTH_URL = "https://accounts.google.com/embedded/reauth/v2/android";
private static final String PROGRAMMATIC_AUTH_URL = "https://accounts.google.com/o/oauth2/programmatic_auth";
private static final String GOOGLE_SUITE_URL = "https://accounts.google.com/signin/continue";
private static final String MAGIC_USER_AGENT = " MinuteMaid";
Expand All @@ -106,6 +109,8 @@ public class LoginActivity extends AssistantActivity {
private InputMethodManager inputMethodManager;
private ViewGroup authContent;
private int state = 0;
private boolean isReAuth = false;
private Account reAuthAccount;

@SuppressLint("AddJavascriptInterface")
@Override
Expand Down Expand Up @@ -148,6 +153,10 @@ public void onPageFinished(WebView view, String url) {
response = (AccountAuthenticatorResponse) tempObject;
}
}
if (getIntent().hasExtra(EXTRA_RE_AUTH_ACCOUNT)) {
reAuthAccount = getIntent().getParcelableExtra(EXTRA_RE_AUTH_ACCOUNT);
isReAuth = reAuthAccount != null;
}
if (getIntent().hasExtra(EXTRA_TOKEN)) {
if (getIntent().hasExtra(EXTRA_EMAIL)) {
AccountManager accountManager = AccountManager.get(this);
Expand All @@ -160,7 +169,7 @@ public void onPageFinished(WebView view, String url) {
} else {
retrieveRtToken(getIntent().getStringExtra(EXTRA_TOKEN));
}
} else if (android.os.Build.VERSION.SDK_INT < 21) {
} else if (android.os.Build.VERSION.SDK_INT < 21 || isReAuth) {
init();
} else {
setMessage(R.string.auth_before_connect);
Expand Down Expand Up @@ -320,26 +329,10 @@ private void retrieveRtToken(String oAuthToken) {
@Override
public void onResponse(AuthResponse response) {
Account account = new Account(response.email, accountType);
if (accountManager.addAccountExplicitly(account, response.token, null)) {
accountManager.setAuthToken(account, "SID", response.Sid);
accountManager.setAuthToken(account, "LSID", response.LSid);
accountManager.setUserData(account, "flags", "1");
accountManager.setUserData(account, "services", response.services);
accountManager.setUserData(account, "oauthAccessToken", "1");
accountManager.setUserData(account, "firstName", response.firstName);
accountManager.setUserData(account, "lastName", response.lastName);
if (!TextUtils.isEmpty(response.accountId))
accountManager.setUserData(account, "GoogleUserId", response.accountId);

retrieveGmsToken(account);
setResult(RESULT_OK);
if (isReAuth && reAuthAccount != null && reAuthAccount.name.equals(account.name)) {
accountManager.removeAccount(account, future -> saveAccount(account, response), null);
} else {
Log.w(TAG, "Account NOT created!");
runOnUiThread(() -> {
showError(R.string.auth_general_error_desc);
setNextButtonText(android.R.string.ok);
});
state = -2;
saveAccount(account, response);
}
}

Expand All @@ -354,7 +347,35 @@ public void onException(Exception exception) {
}
});
}

private void saveAccount(Account account, AuthResponse response) {
if (accountManager.addAccountExplicitly(account, response.token, null)) {
accountManager.setAuthToken(account, "SID", response.Sid);
accountManager.setAuthToken(account, "LSID", response.LSid);
accountManager.setUserData(account, "flags", "1");
accountManager.setUserData(account, "services", response.services);
accountManager.setUserData(account, "oauthAccessToken", "1");
accountManager.setUserData(account, "firstName", response.firstName);
accountManager.setUserData(account, "lastName", response.lastName);
if (!TextUtils.isEmpty(response.accountId))
accountManager.setUserData(account, "GoogleUserId", response.accountId);

retrieveGmsToken(account);
setResult(RESULT_OK);
} else {
Log.w(TAG, "Account NOT created!");
runOnUiThread(() -> {
showError(R.string.auth_general_error_desc);
setNextButtonText(android.R.string.ok);
});
state = -2;
}
}

private void returnSuccessResponse(Account account){
if (isReAuth && reAuthAccount != null) {
AccountNotificationKt.cancelAccountNotificationChannel(this, reAuthAccount);
}
if(response != null){
Bundle bd = new Bundle();
bd.putString(AccountManager.KEY_ACCOUNT_NAME,account.name);
Expand Down Expand Up @@ -426,16 +447,20 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
return super.onKeyDown(keyCode, event);
}

private static String buildUrl(String tmpl, Locale locale) {
return Uri.parse(EMBEDDED_SETUP_URL).buildUpon()
private String buildUrl(String tmpl, Locale locale) {
String uriString = isReAuth ? EMBEDDED_RE_AUTH_URL : EMBEDDED_SETUP_URL;
Uri.Builder builder = Uri.parse(uriString).buildUpon()
.appendQueryParameter("source", "android")
.appendQueryParameter("xoauth_display_name", "Android Device")
.appendQueryParameter("lang", locale.getLanguage())
.appendQueryParameter("cc", locale.getCountry().toLowerCase(Locale.US))
.appendQueryParameter("langCountry", locale.toString().toLowerCase(Locale.US))
.appendQueryParameter("hl", locale.toString().replace("_", "-"))
.appendQueryParameter("tmpl", tmpl)
.build().toString();
.appendQueryParameter("tmpl", tmpl);
if (isReAuth && reAuthAccount != null) {
builder.appendQueryParameter("Email", reAuthAccount.name);
}
return builder.build().toString();
}

private class JsBridge {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,47 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.google.android.gms.R
import org.microg.gms.auth.login.LoginActivity

private const val CHANNEL_ID = "AccountNotification"

@RequiresApi(21)
fun Context.sendAccountReAuthNotification(account: Account) {
Log.d(TAG, "sendAccountReAuthNotification: account: ${account.name}")

registerAccountNotificationChannel()

val intent = Intent(this, LoginActivity::class.java).apply {
putExtra(LoginActivity.EXTRA_RE_AUTH_ACCOUNT, account)
}.let {
PendingIntent.getActivity(
this, account.hashCode(), it, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
)
}

val notification: Notification =
NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_manage_accounts)
.setSound(null)
.setContentTitle(getString(R.string.auth_action_reauth_notification_title))
.setContentText(account.name)
.setOnlyAlertOnce(true)
.setContentIntent(intent)
.setAutoCancel(true)
.build()

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
PackageManager.PERMISSION_GRANTED
) {
NotificationManagerCompat.from(this).notify(account.hashCode(), notification)
}
}

@RequiresApi(21)
fun Context.sendAccountActionNotification(account: Account, action: UserSatisfyRequirements) {
Expand Down Expand Up @@ -67,4 +100,8 @@ fun Context.registerAccountNotificationChannel() {
getSystemService(NotificationManager::class.java)
.createNotificationChannel(channel)
}
}

fun Context.cancelAccountNotificationChannel(account: Account) {
NotificationManagerCompat.from(this).cancel(account.hashCode())
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package org.microg.gms.accountaction

import android.accounts.Account
import android.content.Context
import android.os.Build
import android.content.Intent
import android.os.Build.VERSION.SDK_INT
import android.util.Log
import kotlinx.coroutines.runBlocking
import org.microg.gms.auth.login.LoginActivity
import org.microg.gms.common.Constants
import org.microg.gms.cryptauth.isLockscreenConfigured
import org.microg.gms.cryptauth.sendDeviceScreenlockState
Expand Down Expand Up @@ -39,6 +41,8 @@ const val DEVICE_MANAGEMENT_ADMIN_PENDING_APPROVAL = "DeviceManagementAdminPendi
*/
const val BAD_AUTHENTICATION = "BadAuthentication"

const val SERVER_ERROR = "Error 500"

const val TAG = "GmsAccountErrorResolve"

/**
Expand All @@ -47,6 +51,8 @@ const val TAG = "GmsAccountErrorResolve"
*/
fun Context.resolveAuthErrorMessage(s: String): Resolution? = if (s.startsWith("Error=")) {
resolveAuthErrorMessage(s.drop("Error=".length))
} else if (s.contains(SERVER_ERROR)) {
Reauthenticate
} else when (s) {
DEVICE_MANAGEMENT_SCREENLOCK_REQUIRED -> listOf(
Requirement.ENABLE_CHECKIN,
Expand Down Expand Up @@ -123,13 +129,16 @@ fun <T> Resolution.initiateFromBackgroundBlocking(context: Context, account: Acc
}
is UserSatisfyRequirements -> {
Log.w(TAG, "User intervention required! You need to ${actions.joinToString(", ")}.")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (SDK_INT >= 21) {
context.sendAccountActionNotification(account, this)
}
return null
}
Reauthenticate -> {
Log.w(TAG, "Your account credentials have expired! Please remove the account, then sign in again.")
if (SDK_INT >= 21) {
context.sendAccountReAuthNotification(account)
}
return null
}
}
Expand All @@ -150,7 +159,7 @@ fun <T> Resolution.initiateFromForegroundBlocking(context: Context, account: Acc
}
is UserSatisfyRequirements -> {
Log.w(TAG, "User intervention required! You need to ${actions.joinToString(", ")}.")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (SDK_INT >= 21) {
AccountActionActivity.createIntent(context, account, this).let {
context.startActivity(it)
}
Expand All @@ -159,6 +168,11 @@ fun <T> Resolution.initiateFromForegroundBlocking(context: Context, account: Acc
}
Reauthenticate -> {
Log.w(TAG, "Your account credentials have expired! Please remove the account, then sign in again.")
Intent(context, LoginActivity::class.java).apply {
putExtra(LoginActivity.EXTRA_RE_AUTH_ACCOUNT, account)
}.let {
context.startActivity(it)
}
return null
}
}
Expand Down
1 change: 1 addition & 0 deletions play-services-core/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ microG GmsCore 内置一套自由的 SafetyNet 实现,但是官方服务器要
<string name="auth_action_notification_content">你的 Google 账户需要额外设置。</string>
<string name="auth_action_activity_explanation">要能在这台设备上使用你的 Google 账户 %s 请完成下列步骤。</string>
<string name="auth_action_step_enable_lockscreen_description">你的 Google 账户受工作场所或教育机构管理。你的管理员决定设备在可以访问账户数据前需要设置安全屏幕锁。\n\n请设置一个密码、PIN或手势屏幕锁。</string>
<string name="auth_action_reauth_notification_title">需要执行账号相关操作</string>
<string name="barcode_scanner_brand">由 microG 代表“%1$s”扫描</string>
<string name="camera_permission_dialog_button">确定</string>
<string name="camera_permission_dialog_message">microG 服务需要访问设备的摄像头,才能为%1$s扫描二维码。\n\n若要启用该权限,请在“设置”中向 microG 服务授予相机权限。</string>
Expand Down
1 change: 1 addition & 0 deletions play-services-core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ This can take a couple of minutes."</string>
<string name="auth_action_notification_title">Account action required</string>
<string name="auth_action_notification_content">Your Google account needs additional setup.</string>

<string name="auth_action_reauth_notification_title">Account action required</string>
<string name="auth_action_activity_header">Finish setting up your Google account</string>
<string name="auth_action_activity_explanation">Complete the following steps to be able to use your Google account %s on this device.</string>
<string name="auth_action_step_enable_checkin">Enable device registration</string>
Expand Down