diff --git a/play-services-auth-workaccount/core/src/main/kotlin/com/google/android/gms/auth/account/authenticator/WorkAccountAuthenticator.kt b/play-services-auth-workaccount/core/src/main/kotlin/com/google/android/gms/auth/account/authenticator/WorkAccountAuthenticator.kt index 62e31a3f61..b5f3ff97e9 100644 --- a/play-services-auth-workaccount/core/src/main/kotlin/com/google/android/gms/auth/account/authenticator/WorkAccountAuthenticator.kt +++ b/play-services-auth-workaccount/core/src/main/kotlin/com/google/android/gms/auth/account/authenticator/WorkAccountAuthenticator.kt @@ -38,74 +38,52 @@ class WorkAccountAuthenticator(val context: Context) : AbstractAccountAuthentica authTokenType: String?, requiredFeatures: Array?, options: Bundle - ): Bundle? { - - if (!WorkProfileSettings(context).allowCreateWorkAccount) { - return Bundle().apply { - putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION) - putString(AccountManager.KEY_ERROR_MESSAGE, context.getString(R.string.auth_work_authenticator_disabled_error) - ) - } - } else if ( - !options.containsKey(KEY_ACCOUNT_CREATION_TOKEN) - || options.getString(KEY_ACCOUNT_CREATION_TOKEN) == null - || options.getInt(AccountManager.KEY_CALLER_UID) != android.os.Process.myUid()) { - Log.e(TAG, - "refusing to add account without creation token or from external app: " + - "could have been manually initiated by user (not supported) " + - "or by unauthorized app (not allowed)" + ): Bundle { + /* Calls to this method are always initiated by other applications or by the user. + * We refuse, because `accountCreationToken` is needed, and because only profile owner is + * supposed to provision this account. Profile owner will use `WorkAccountAuthenticator` + * instead, which calls the code in `addAccountInternal` directly. + * + * Also note: adding account with `AccountManager.addAccount` can be forbidden by device + * policy. + */ + return Bundle().apply { + putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION) + putString( + AccountManager.KEY_ERROR_MESSAGE, + context.getString(R.string.auth_work_authenticator_add_manual_error) ) - - // TODO: The error message is not automatically displayed by the settings app as of now. - // We can consider showing the error message through a popup instead. - - return Bundle().apply { - putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION) - putString(AccountManager.KEY_ERROR_MESSAGE, context.getString(R.string.auth_work_authenticator_add_manual_error) - ) - } } + } - val oauthToken: String = options.getString(KEY_ACCOUNT_CREATION_TOKEN)!! + /** + * @return `null` if account creation fails, the newly created account otherwise + */ + fun addAccountInternal( + accountCreationToken: String + ): Account? { - try { - tryAddAccount(oauthToken, response) - } catch (exception: Exception) { - response.onResult(Bundle().apply { - putInt( - AccountManager.KEY_ERROR_CODE, - AccountManager.ERROR_CODE_NETWORK_ERROR - ) - putString(AccountManager.KEY_ERROR_MESSAGE, exception.message) - }) + if (!WorkProfileSettings(context).allowCreateWorkAccount) { + // TODO: communicate error to user (use `R.string.auth_work_authenticator_disabled_error`) + Log.w(TAG, "creating a work account is disabled in microG settings") + return null } - /* Note: as is not documented, `null` must only be returned after `response.onResult` was - * already called, hence forcing the requests to be synchronous. They are still async to - * the caller's main thread because AccountManager forces potentially blocking operations, - * like waiting for a response upon `addAccount`, not to be on the main thread. - */ - return null - } - - @Throws(Exception::class) - private fun tryAddAccount( - oauthToken: String, - response: AccountAuthenticatorResponse - ) { - val authResponse = AuthRequest().fromContext(context) - .appIsGms() - .callerIsGms() - .service("ac2dm") - .token(oauthToken).isAccessToken() - .addAccount() - .getAccountId() - .droidguardResults(null) - .response - - val accountManager = AccountManager.get(context) - if (accountManager.addAccountExplicitly( - Account(authResponse.email, AuthConstants.WORK_ACCOUNT_TYPE), + return try { + val authResponse = AuthRequest().fromContext(context) + .appIsGms() + .callerIsGms() + .service("ac2dm") + .token(accountCreationToken).isAccessToken() + .addAccount() + .getAccountId() + .droidguardResults("null") // TODO + .response + + val accountManager = AccountManager.get(context) + val account = Account(authResponse.email, AuthConstants.WORK_ACCOUNT_TYPE) + val accountAdded = accountManager.addAccountExplicitly( + account, authResponse.token, Bundle().apply { // Work accounts have no SID / LSID ("BAD_COOKIE") and no first/last name. if (authResponse.accountId.isNotBlank()) { @@ -119,20 +97,21 @@ class WorkAccountAuthenticator(val context: Context) : AbstractAccountAuthentica "unexpected 'services' value ${authResponse.services} (usually 'android')" ) } - } - ) - ) { + }) - // Notify vending package - context.sendBroadcast( - Intent(WORK_ACCOUNT_CHANGED_BOARDCAST).setPackage("com.android.vending") - ) + if (accountAdded) { - // Report successful creation to caller - response.onResult(Bundle().apply { - putString(AccountManager.KEY_ACCOUNT_NAME, authResponse.email) - putString(AccountManager.KEY_ACCOUNT_TYPE, AuthConstants.WORK_ACCOUNT_TYPE) - }) + // Notify vending package + context.sendBroadcast( + Intent(WORK_ACCOUNT_CHANGED_BOARDCAST).setPackage("com.android.vending") + ) + + // Report successful creation to caller + account + } else null + } catch (exception: Exception) { + Log.w(TAG, "Failed to add work account.", exception) + null } } @@ -234,7 +213,6 @@ class WorkAccountAuthenticator(val context: Context) : AbstractAccountAuthentica const val WORK_ACCOUNT_CHANGED_BOARDCAST = "org.microg.vending.WORK_ACCOUNT_CHANGED" - const val KEY_ACCOUNT_CREATION_TOKEN = "creationToken" private const val KEY_GOOGLE_USER_ID = AuthConstants.GOOGLE_USER_ID } } \ No newline at end of file diff --git a/play-services-auth-workaccount/core/src/main/kotlin/org/microg/gms/auth/workaccount/WorkAccountService.kt b/play-services-auth-workaccount/core/src/main/kotlin/org/microg/gms/auth/workaccount/WorkAccountService.kt index 21c3b77d21..4aa09a1542 100644 --- a/play-services-auth-workaccount/core/src/main/kotlin/org/microg/gms/auth/workaccount/WorkAccountService.kt +++ b/play-services-auth-workaccount/core/src/main/kotlin/org/microg/gms/auth/workaccount/WorkAccountService.kt @@ -13,12 +13,11 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Build.VERSION.SDK_INT -import android.os.Bundle import android.os.Parcel import android.util.Log import com.google.android.gms.auth.account.IWorkAccountCallback import com.google.android.gms.auth.account.IWorkAccountService -import com.google.android.gms.auth.account.authenticator.WorkAccountAuthenticator.Companion.KEY_ACCOUNT_CREATION_TOKEN +import com.google.android.gms.auth.account.authenticator.WorkAccountAuthenticator import com.google.android.gms.auth.account.authenticator.WorkAccountAuthenticator.Companion.WORK_ACCOUNT_CHANGED_BOARDCAST import com.google.android.gms.auth.account.authenticator.WorkAccountAuthenticatorService import com.google.android.gms.common.Feature @@ -27,7 +26,6 @@ import com.google.android.gms.common.internal.ConnectionInfo import com.google.android.gms.common.internal.GetServiceRequest import com.google.android.gms.common.internal.IGmsCallbacks import org.microg.gms.BaseService -import org.microg.gms.auth.AuthConstants import org.microg.gms.common.GmsService import org.microg.gms.common.PackageUtils @@ -97,30 +95,12 @@ class WorkAccountServiceImpl(val context: Context) : IWorkAccountService.Stub() override fun addWorkAccount( callback: IWorkAccountCallback?, - token: String? + token: String ) { Log.d(TAG, "addWorkAccount with token $token") - val future = accountManager.addAccount( - AuthConstants.WORK_ACCOUNT_TYPE, - null, - null, - Bundle().apply { putString(KEY_ACCOUNT_CREATION_TOKEN, token) }, - null, - null, - null - ) Thread { - try { - future.result.let { result -> - callback?.onAccountAdded( - Account( - result.getString(AccountManager.KEY_ACCOUNT_NAME)!!, - result.getString(AccountManager.KEY_ACCOUNT_TYPE)!! - ) - ) - } - } catch (e: Exception) { - Log.e(TAG, "could not add work account with error message: ${e.message}") + WorkAccountAuthenticator(context).addAccountInternal(token)?.let { + callback?.onAccountAdded(it) } }.start() }