Google Drive API - the name must not be empty: null (But I had passed valid account name to GoogleAccountCredential)

11,449

Solution 1

It looks like the Drive API Client Library for Java calls into GoogleAuthUtil.getToken(), which requires the GET_ACCOUNTS permission. You need to have that permission in your manifest and request it at runtime, as appropriate.

UPDATE: With Google Drive REST v3 API, GET_ACCOUNTS permission is not required. Instead, Email permission is required. You can ask for it by calling GoogleSignInOptions.Builder.requestEmail.

Solution 2

I had the same problem, all permissions were granted and still I got this error.

My simple problem was, that the googleSignInAccount.getSignInAccount().getAccount() was null. This is the case, if you do NOT request the email permissions (GoogleSignInOptions.Builder.requestEmail). Seems like this account can't be created without the user email.

So make sure to add this permission to the GoogleSignInOptions...

Solution 3

This is because from Android 6.0 (API level 23) you need to get the Contacts permission before you invoke google endpoints.

You can see complete details here - https://developer.android.com/training/permissions/requesting.html

This is true only for Dangerous permissions and permission groups (based on how much they are related to user privacy). More details - https://developer.android.com/guide/topics/security/permissions.html#perm-groups

Solution 4

In my case (Google Drive REST API v3), ProGuard was the culprit, as the code was working fine in debug mode.

Just adding -keep class com.google.** { *;} to ProGuard rules got rid of the issue.

PS: I had to call requestEmail() but I did not ask for any GET_ACCOUNTS permission.

Solution 5

I use google api client

// Dependency of Google Api client
implementation 'com.google.api-client:google-api-client:1.30.9'
implementation 'com.google.api-client:google-api-client-android:1.30.9'
implementation 'com.google.apis:google-api-services-drive:v3-rev20200413-1.30.9'
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestScopes(new Scope(Scopes.DRIVE_APPFOLDER))
                //requestEmail required
                .requestEmail()
                .build();
GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(this, gso);


 // in onActivityResult method
 Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
 GoogleSignInAccount account = completedTask.getResult(ApiException.class);
 // androidAccount will be null if no email
 android.accounts.Account androidAccount = account.getAccount(); 

You can see it from the source code of android.accounts.Account

  private String zag;
    @Field(
        id = 4,
        getter = "getEmail"
    )
    private String zah;

    @Nullable
    public Account getAccount() {
        // getEmail required
        return this.zah == null ? null : new Account(this.zah, "com.google");
    }

Share:
11,449

Related videos on Youtube

Lumii
Author by

Lumii

WeNote - Notes, To-do lists, Reminders &amp; Calendar JStock Android JStock - Free Stock Market Software WeFocus - Focus, Pomodoro, Do one thing at a time

Updated on September 15, 2022

Comments

  • Lumii
    Lumii over 1 year

    Recently, I have Android code which accesses to Google Drive. I'm using Google APIs Client Library for Java instead of Google Play services client library

    private static GoogleCloudFile searchFromGoogleDrive(Drive drive, String qString, HandleUserRecoverableAuthIOExceptionable h, PublishProgressable p) {
        try {
            Files.List request = drive.files().list().setQ(qString);
    
            do {        
                if (p.isCancelled()) {
                    return null;
                }
    
                FileList fileList = request.execute();
    

    The code works 100% fine for several years, if I use targetSdkVersion 21.

    Recently, I migrate my app to targetSdkVersion 23, with 0 change on Google Drive related code.

    However, the code crashes mystery at FileList fileList = request.execute();, with the following exception.

    Error log while using 1.18.0-rc

    Process: org.yccheok.jstock.gui, PID: 30317
    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:309)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
        at java.util.concurrent.FutureTask.run(FutureTask.java:242)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
        at java.lang.Thread.run(Thread.java:818)
     Caused by: java.lang.IllegalArgumentException: the name must not be empty: null
        at android.accounts.Account.<init>(Account.java:48)
        at com.google.android.gms.auth.zzd.getToken(Unknown Source)
        at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
        at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential.getToken(GoogleAccountCredential.java:255)
        at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential$RequestHandler.intercept(GoogleAccountCredential.java:279)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:859)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:410)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:343)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:460)
        at org.yccheok.jstock.gui.Utils.searchFromGoogleDrive(Utils.java:208)
        at org.yccheok.jstock.gui.Utils._loadFromGoogleDrive(Utils.java:277)
        at org.yccheok.jstock.gui.Utils.loadFromGoogleDrive(Utils.java:344)
        at org.yccheok.jstock.gui.LoadFromCloudTask.doInBackground(LoadFromCloudTask.java:23)
        at org.yccheok.jstock.gui.LoadFromCloudTask.doInBackground(LoadFromCloudTask.java:9)
        at android.os.AsyncTask$2.call(AsyncTask.java:295)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
        at java.lang.Thread.run(Thread.java:818) 
    

    If I google, I get Google Drive on android error: java.lang.IllegalArgumentException: the name must not be empty: null , which states that account name is not passed to credential object.

    However, I'm pretty much sure that my credential object is passed with valid account name (in format [email protected])

    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        switch (requestCode) {
        case RequestCode.REQUEST_ACCOUNT_PICKER_LOAD_FROM_CLOUD:
            if (resultCode == RESULT_OK && data != null && data.getExtras() != null) {
                String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
                Log.i("CHEOK", "accountName = " + accountName);
                if (accountName != null) {
                    JStockApplication.instance().getJStockOptions().setUsername(accountName);
                    Utils.getGoogleAccountCredential().setSelectedAccountName(accountName);
    

    Utils.java

    public static GoogleAccountCredential getGoogleAccountCredential() {
        return googleAccountCredential;
    }
    
    private static final GoogleAccountCredential googleAccountCredential = GoogleAccountCredential.usingOAuth2(JStockApplication.instance(), 
        Arrays.asList(
            DriveScopes.DRIVE_APPDATA,
            // Legacy. Shall be removed after a while...
            DriveScopes.DRIVE
        )
    );
    
    private Drive getDriveService() {
        return new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), Utils.getGoogleAccountCredential()).build();
    }
    

    Also, I'm pretty sure that my OAuth 2.0 correctly through Google Developers Console (For both debug keystore & production keystore) Recently, Android 6 introduces run-time permissions. I was wondering, can this exception related to that?

    I further try with latest library version 1.21.0. However, I still get similar error log

    Error log while using 1.21.0

    java.lang.RuntimeException: An error occurred while executing doInBackground()
        at android.os.AsyncTask$3.done(AsyncTask.java:309)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
        at java.util.concurrent.FutureTask.run(FutureTask.java:242)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
        at java.lang.Thread.run(Thread.java:818)
    Caused by: java.lang.IllegalArgumentException: the name must not be empty: null
        at android.accounts.Account.<init>(Account.java:48)
        at com.google.android.gms.auth.zzd.getToken(Unknown Source)
        at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
        at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential.getToken(GoogleAccountCredential.java:255)
        at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential$RequestHandler.intercept(GoogleAccountCredential.java:279)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:859)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
        at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
        at org.yccheok.jstock.gui.Utils.searchFromGoogleDrive(Utils.java:208)
        at org.yccheok.jstock.gui.Utils._loadFromGoogleDrive(Utils.java:277)
        at org.yccheok.jstock.gui.Utils.loadFromGoogleDrive(Utils.java:344)
        at org.yccheok.jstock.gui.LoadFromCloudTask.doInBackground(LoadFromCloudTask.java:23)
        at org.yccheok.jstock.gui.LoadFromCloudTask.doInBackground(LoadFromCloudTask.java:9)
        at android.os.AsyncTask$2.call(AsyncTask.java:295)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
        at java.lang.Thread.run(Thread.java:818) 
    

    Update on my further testing

    The above 2 error logs are generated while I'm testing using Nexus 5, Android version 6.0.1

    However, if I conduct the same testing using Nexus 4, Android version 5.1.1, no error occurs! Both 1.18.0-rc and 1.21.0 are working fine

    Hence, it seems the crash is strongly related to Android version.

  • Lumii
    Lumii over 8 years
    Oh well. I don't think I can explain to user clearly, why I need to access their contacts information in order to access their Google Drive content. I rather not take the risk of having to trigger the nuclear option (Don't ask again). Do you think Google can provide a way to by-pass such restriction? Meanwhile, going to try to migrate the code to Play services to see it helps.
  • ianhanniballake
    ianhanniballake over 8 years
    @CheokYanCheng - yes, I'd encourage you to manually grant yourself the 'Contacts' permission group (which includes GET_ACCOUNTS) to ensure that is the issue, but using Google Play services is definitely highly recommended for Android apps (particularly for easy authentication).
  • Lumii
    Lumii over 8 years
    After testing by manual granting, I can pretty confirm this problem is caused by not having GET_ACCOUNTS permission. Is there any way which Google Drive team can design the API in the way which doesn't require GET_ACCOUNTS run-time permission? The reason I'm asking so is that, after experimenting for few days, I realize GDAA doesn't handle some edge cases as good as REST Api such as stackoverflow.com/q/33248420/72437 This makes migration to GDAA difficult.
  • ianhanniballake
    ianhanniballake over 8 years
    From the Google Api Client page on supported devices: " if a Google Play Services library is available for the Google service you need, use that library instead of this one."
  • chenghuayang
    chenghuayang almost 7 years
    I just met this problem when setting the automatically uploading function on one app. Once I saw the answer, I logged out and signed in again. It works. Just add this as a reminder for those who just meet the problem dialog when using app.
  • Jürgen Jatzkowski
    Jürgen Jatzkowski over 6 years
    In my case it was required (beside requesting email permissions) to switch from credential.setSelectedAccountName() to credential.setSelectedAccount()
  • Roman Samoilenko
    Roman Samoilenko almost 6 years
    It would still crash on Android Oreo because GET_ACCOUNTS became deprecated. What to do in such case?
  • Alin
    Alin about 5 years
    Thanks! It seems like the GET_ACCOUNTS permission is NOT required for Google Drive REST v3 API. But calling requestEmail() is required
  • Merk
    Merk over 4 years
    This is not a sensible explanation. My app is using its own Drive AppData folder only. The associated scope is not one of the ones marked dangerous, and there is no good reason why the REST v3 API should require the user email when the deprecated Android Drive SDK does not. Isn't one of the purposes of OAuth (where the problem is triggered) to hide certain properties of agents (e.g., ultimate email address) to systems that don't need to know? Seems like a design problem on Google's part.