targetSdkVersion 23 returns 0 length array via accountManager.getAccounts()
Asked Answered
A

1

9

I observed the following strange outcome in my real Nexus 5 device, Android 6.0.1

I run the following simple code during my app launched.

Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
AccountManager accountManager = AccountManager.get(this);
Account[] accounts = accountManager.getAccounts();

The above code works fine, if I set my targetSdkVersion to 22, 21, 19. It returns non empty array.

However, when I change targetSdkVersion and tested with

defaultConfig {
    applicationId "org.yccheok.myapplication"
    minSdkVersion 19
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"
}

The above code returns 0 length array!

Any idea why thing breaks when compiled against targetSdkVersion=23.

Note, during to produce the problem, whenever you change the targetSdkVersion and run it through Android Studio, you need to clear your app data, clear your app cache and uninstall for all users manually.

Here are steps to reproduce the problem in Nexus 5 device, Android 6.0.1

  1. Create project in Android Studio via File -> New -> New Projetct...
  2. Choose "Blank Activity"

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.yccheok.myapplication">

    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "org.yccheok.myapplication"
        minSdkVersion 19
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:design:23.1.1'
}

MainActivity.java

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Patterns;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.util.regex.Pattern;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        int targetSdkVersion = 0;
        try {
            PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
            targetSdkVersion = packageInfo.applicationInfo.targetSdkVersion;
        }
        catch (PackageManager.NameNotFoundException e) {
            android.util.Log.i("CHEOK", e.getMessage());
        }

        Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
        AccountManager accountManager = AccountManager.get(this);
        Account[] accounts = accountManager.getAccounts();

        android.util.Log.i("CHEOK", targetSdkVersion + " : numnber of accoutn by ??? = " + accounts.length);

        for (Account account : accounts) {
            if (emailPattern.matcher(account.name).matches()) {
                String possibleEmail = account.name;
                android.util.Log.i("CHEOK", "possibleEmail = " + possibleEmail);
            }
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Any idea why targetSdkVersion 23 returns 0 length array via accountManager.getAccounts()

Apodosis answered 1/1, 2016 at 23:41 Comment(1)
Might it have to do with the new permission system? Perhaps you would need to request the appropiate permission at runtime and the call fails gracefully? Just a shot in the dark.Heddi
S
4

GET_ACCOUNTS is a dangerous permission and when using target sdk 23 needs to be managed with runtime permissions or it will not work.

You need to actually request permission from the user at runtime. example:

      ActivityCompat.requestPermissions(this, new String[] 
      {Manifest.permission.GET_ACCOUNTS}, 1);

      int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
      Manifest.permission.GET_ACCOUNTS);

I've gone into detail about this in this answer here.

Dangerous permissions cover areas where the app wants data or resources that involve the user's private information, or could potentially affect the user's stored data or the operation of other apps. For example, the ability to read the user's contacts is a dangerous permission. If an app declares that it needs a dangerous permission, the user has to explicitly grant the permission to the app.

Important Note:
As stated in comment below, READ_CONTACTS permission is also MANDATORY.
If READ_CONTACTS permission is not obtained, accountManager.getAccounts() will return an empty array.

Sisley answered 2/1, 2016 at 0:57 Comment(6)
I still get 0 returned array size even with all this and I am targeting API 27Bronk
@Bronk you'll need to post a self contained question with any relevant details. It's impossible to answer in the comments.Sisley
I've seen this in 2 different apps I have coded with no solution. I will post a separate question, but I fear if I do others will just point to this solution.Bronk
@Bronk explain what you've tried and how this solution has not worked.Sisley
@Bronk Ask them for READ_CONTACTS permission, it will return the list of accounts.Passionless
I have improved the answer, I think the key piece of sample code to request explicit permission was missing. It should be more clear now.Tiffinytiffy

© 2022 - 2024 — McMap. All rights reserved.