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
<?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>
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'
}
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()
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.