How to merge emailAndPasswordAuth with PhoneAuth in Firebase?

I guess you are doing it the wrong way.

The flow as mentioned in documentation:

Complete the sign-in flow for the new authentication provider up to, but not including, calling one of the FirebaseAuth.signInWith methods. For example, get the user's Google ID token, Facebook access token, or email and password.

As quoted from the documentation of linking auth provider steps, it is mentioned that you should not call any FirebaseAuth.signInWith methods, rather you need to:-

  1. get AuthCredential for the new authentication provider
  2. Pass the AuthCredential object to the signed-in user's linkWithCredential method, like this:

    mAuth.getCurrentUser().linkWithCredential(credential)

As user is already signed with one auth provider, we don't need to sign him again. We just need to link both the providers, so that he'll be able to sign in again with either of the providers.

As the flow from your code suggests, after verifying the phone number, you are signing the user in again with PhoneAuthCredential, and then you are trying to link the emailCredential; which the current signed in user is already linked to, hence the error.

This should be your code for mCallbacks:

mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
        @Override
        public void onVerificationCompleted(PhoneAuthCredential credential) {
            Log.d(TAG, "onVerificationCompleted:" + credential);
            linkCredential(credential);
        }

        @Override
        public void onVerificationFailed(FirebaseException e) {
            Log.w(TAG, "onVerificationFailed", e);
            if (e instanceof FirebaseAuthInvalidCredentialsException) {
                mPhoneNumberField.setError("Invalid phone number.");
            } else if (e instanceof FirebaseTooManyRequestsException) {
                Snackbar.make(findViewById(android.R.id.content), "Quota exceeded.",
                        Snackbar.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onCodeSent(String verificationId,
                               PhoneAuthProvider.ForceResendingToken token) {
            Log.d(TAG, "onCodeSent:" + verificationId);
            mVerificationId = verificationId;
            mResendToken = token;
        }
    };

And Here is the linkCredentials method.

    public void linkCredential(AuthCredential credential) {
    mAuth.getCurrentUser().linkWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "linkWithCredential:success");
                        FirebaseUser user = task.getResult().getUser();
                        Toast.makeText(PhoneActivity.this, "Merged", Toast.LENGTH_SHORT).show();
                        moveToHome();

                    } else {
                        Log.w(TAG, "linkWithCredential:failure", task.getException());
                        Toast.makeText(PhoneActivity.this, "Failed to merge" + task.getException().toString(), Toast.LENGTH_SHORT).show();
                    }
                }
            });
}