aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/google/android/mobly/snippet/bundled/AccountSnippet.java
diff options
context:
space:
mode:
authorAlexander Dorokhine <adorokhine@google.com>2017-02-13 19:02:21 -0800
committerGitHub <noreply@github.com>2017-02-13 19:02:21 -0800
commitc82a5a36f1b616b0d69488512da6d9054340b8da (patch)
tree5ed7c9f0634cff32124382de3e1407b1af73fe63 /src/main/java/com/google/android/mobly/snippet/bundled/AccountSnippet.java
parent9ee1c6ed1a90157e0d0d6c9b23ad69d75a178ec7 (diff)
downloadmobly-bundled-snippets-c82a5a36f1b616b0d69488512da6d9054340b8da.tar.gz
Support adding and listing Google accounts. (#16)
Diffstat (limited to 'src/main/java/com/google/android/mobly/snippet/bundled/AccountSnippet.java')
-rw-r--r--src/main/java/com/google/android/mobly/snippet/bundled/AccountSnippet.java156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/main/java/com/google/android/mobly/snippet/bundled/AccountSnippet.java b/src/main/java/com/google/android/mobly/snippet/bundled/AccountSnippet.java
new file mode 100644
index 0000000..48b70fc
--- /dev/null
+++ b/src/main/java/com/google/android/mobly/snippet/bundled/AccountSnippet.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.mobly.snippet.bundled;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AccountsException;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SyncAdapterType;
+import android.content.SyncStatusObserver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.test.InstrumentationRegistry;
+import android.widget.Toast;
+import com.google.android.mobly.snippet.Snippet;
+import com.google.android.mobly.snippet.rpc.Rpc;
+import com.google.android.mobly.snippet.util.Log;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Snippet class exposing Android APIs related to management of device accounts.
+ *
+ * <p>Android devices can have accounts of any type added and synced. New types can be created by
+ * apps by implementing a {@link android.content.ContentProvider} for a particular account type.
+ *
+ * <p>Google (gmail) accounts are of type "com.google" and their handling is managed by the
+ * operating system. This class allows you to add and remove Google accounts from a device.
+ * */
+public class AccountSnippet implements Snippet {
+ private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
+ private static final String AUTH_TOKEN_TYPE = "mail";
+
+ private static class AccountSnippetException extends Exception {
+ public AccountSnippetException(String msg) {
+ super(msg);
+ }
+ }
+
+ private final AccountManager mAccountManager;
+ private final List<Object> mSyncStatusObserverHandles;
+
+ public AccountSnippet() {
+ Context context = InstrumentationRegistry.getContext();
+ mAccountManager = AccountManager.get(context);
+ mSyncStatusObserverHandles = new LinkedList<>();
+ }
+
+ /**
+ * Adds a Google account to the device.
+ *
+ * <p>TODO(adorokhine): Support adding accounts of other types with an optional 'type' kwarg.
+ * <p>TODO(adorokhine): Allow users to choose whether to enable/disable sync with a kwarg.
+ *
+ * @param username Username of the account to add (including @gmail.com).
+ * @param password Password of the account to add.
+ */
+ @Rpc(description =
+ "Add a Google (GMail) account to the device, with account data sync disabled.")
+ public void addAccount(String username, String password)
+ throws AccountSnippetException, AccountsException, IOException {
+ // Check for existing account. If we try to re-add an existing account, Android throws an
+ // exception that says "Account does not exist or not visible. Maybe change pwd?" which is
+ // a little hard to understand.
+ if (listAccounts().contains(username)) {
+ throw new AccountSnippetException(
+ "Account " + username + " already exists on the device");
+ }
+ Bundle addAccountOptions = new Bundle();
+ addAccountOptions.putString("username", username);
+ addAccountOptions.putString("password", password);
+ AccountManagerFuture<Bundle> future =
+ mAccountManager.addAccount(
+ GOOGLE_ACCOUNT_TYPE,
+ AUTH_TOKEN_TYPE,
+ null /* requiredFeatures */,
+ addAccountOptions,
+ null /* activity */,
+ null /* authCallback */,
+ null /* handler */);
+ Bundle result = future.getResult();
+ if (result.containsKey(AccountManager.KEY_ERROR_CODE)) {
+ throw new AccountSnippetException(
+ String.format("Failed to add account due to code %d: %s",
+ result.getInt(AccountManager.KEY_ERROR_CODE),
+ result.getString(AccountManager.KEY_ERROR_MESSAGE)));
+ }
+
+ // Disable sync to avoid test flakiness as accounts fetch additional data.
+ // It takes a while for all sync adapters to be populated, so register for broadcasts when
+ // sync is starting and disable them there.
+ // NOTE: this listener is NOT unregistered because several sync requests for the new account
+ // will come in over time.
+ Account account = new Account(username, GOOGLE_ACCOUNT_TYPE);
+ Object handle = ContentResolver.addStatusChangeListener(
+ ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE | ContentResolver.SYNC_OBSERVER_TYPE_PENDING,
+ which -> {
+ Log.i("Attempt to sync account " + username + " detected! Disabling.");
+ for (SyncAdapterType adapter : ContentResolver.getSyncAdapterTypes()) {
+ if (!adapter.accountType.equals(GOOGLE_ACCOUNT_TYPE)) {
+ continue;
+ }
+ ContentResolver
+ .setSyncAutomatically(account, adapter.authority, false /* sync */);
+ ContentResolver.cancelSync(account, adapter.authority);
+ }
+ });
+ mSyncStatusObserverHandles.add(handle);
+ }
+
+ /**
+ * Returns a list of all Google accounts on the device.
+ *
+ * <p>TODO(adorokhine): Support accounts of other types with an optional 'type' kwarg.
+ */
+ @Rpc(description = "List all Google (GMail) accounts on the device.")
+ public Set<String> listAccounts() throws SecurityException {
+ Account[] accounts = mAccountManager.getAccountsByType(GOOGLE_ACCOUNT_TYPE);
+ Set<String> usernames = new TreeSet<>();
+ for (Account account : accounts) {
+ usernames.add(account.name);
+ }
+ return usernames;
+ }
+
+ @Override
+ public void shutdown() {
+ for (Object handle : mSyncStatusObserverHandles) {
+ ContentResolver.removeStatusChangeListener(handle);
+ }
+ }
+}