简体   繁体   English

Android使用AccountManager添加帐户

[英]Android Adding Account with AccountManager

Hey guys im trying to add an account with androids accountManager, I keep getting this stack trace below. 大家好,我想用androids accountManager添加一个帐户,我一直在下面获取此堆栈跟踪。

Guys I cant post all of my code cause I break the limit that Stack Overflow allows me to enter. 伙计们,我无法发布所有代码,因为我突破了Stack Overflow允许我输入的限制。 so ill only post snippets of code you request cause there is WAY more code for this. 所以请只发布您请求的代码片段,因为还有更多的代码可以使用。 Sorry for the messy code im just messing around with it till I can get it to work then ill clean it up. 对不起,我只是把它弄乱了,直到我能使它正常工作,然后再把它清理干净。

FATAL EXCEPTION: main

Process: com.example.rapid.rapid, PID: 6168
java.lang.SecurityException: uid 10335 cannot explicitly add accounts of type: com.example.rapid.rapid
    at android.os.Parcel.readException(Parcel.java:1620)
    at android.os.Parcel.readException(Parcel.java:1573)
    at android.accounts.IAccountManager$Stub$Proxy.addAccountExplicitly(IAccountManager.java:890)
    at android.accounts.AccountManager.addAccountExplicitly(AccountManager.java:716)
    at com.example.rapid.rapid.LoginActivity$1$1.onResponse(LoginActivity.java:174)
    at com.example.rapid.rapid.LoginActivity$1$1.onResponse(LoginActivity.java:140)
    at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:60)
    at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:30)
    at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:158)
    at android.app.ActivityThread.main(ActivityThread.java:7237)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

LoginActivity.java LoginActivity.java

public class LoginActivity extends Activity {
    private static final String TAG = "LoginActivity";
    public final static String ARG_ACCOUNT_TYPE = "com.example.rapid.rapid";
    public final static String ARG_AUTH_TYPE = "AUTH_TYPE";
    public final static String ARG_ACCOUNT_NAME = "com.example.rapid.rapid";
    public final static String ARG_IS_ADDING_NEW_ACCOUNT = "IS_ADDING_ACCOUNT";
    public static final String KEY_ERROR_MESSAGE = "ERR_MSG";
    public final static String PARAM_USER_PASS = "USER_PASS";
    private static final int REQUEST_SIGNUP = 0;
    private AccountManager mAccountManager;
    public static final String ACCOUNT_TYPE = "com.example.rapid.rapid";
    private static final String CONTENT_AUTHORITY = "com.example.rapid.rapid";
    private static final String PREF_SETUP_COMPLETE = "setup_complete";
    private static final long SYNC_FREQUENCY = 60 * 60;  // 1 hour (in seconds)
    private String mAuthTokenType;
    private boolean mInvalidate;
    private AlertDialog mAlertDialog;

    @InjectView(R.id.loginEmailWrapper)
    TextInputLayout _loginEmailWrapper;
    @InjectView(R.id.loginPasswordWrapper)
    TextInputLayout _loginPasswordWrapper;
    @InjectView(R.id.loginEmailInput)
    EditText _loginEmailInput;
    @InjectView(R.id.loginPasswordInput)
    EditText _loginPasswordInput;
    @InjectView(R.id.loginPasswordVisibility)
    ImageView _loginPasswordVisibility;
    @InjectView(R.id.btn_login)
    Button _loginButton;
    @InjectView(R.id.link_signup)
    TextView _signupLink;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /*Uncomment this to make this screen of the app fullscreen.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);*/
        setContentView(R.layout.activity_login);
        ButterKnife.inject(this);
        mAccountManager = AccountManager.get(this);

        boolean setupComplete = PreferenceManager
                .getDefaultSharedPreferences(this.getApplicationContext()).getBoolean(PREF_SETUP_COMPLETE, false);

        String accountName = getIntent().getStringExtra(ARG_ACCOUNT_NAME);
        mAuthTokenType = getIntent().getStringExtra(ARG_AUTH_TYPE);
        if (mAuthTokenType == null)
            mAuthTokenType = AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS;

        if (accountName != null) {
            _loginEmailInput.setText(accountName);
        }

        _loginButton.setOnClickListener(new View.OnClickListener() {


            @Override
            public void onClick(View v) {
                Log.d(TAG, "Begin Login process...");
                showAccountPicker(mAuthTokenType, false);

                if (!validate()) {
                    onLoginFailed();
                    return;
                }

                final String email = _loginEmailInput.getText().toString();
                final String password = _loginPasswordInput.getText().toString();

                final String accountType = getIntent().getStringExtra(ARG_ACCOUNT_TYPE);

                _loginButton.setEnabled(false);

                final ProgressDialog progressDialog = new ProgressDialog(LoginActivity.this,
                        R.style.Theme_IAPTheme);
                progressDialog.setIndeterminate(true);
                progressDialog.setMessage("Authenticating...");
                progressDialog.show();



                // Response received from the server
                Response.Listener<String> responseListener = new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        String authtoken = null;
                        boolean newAccount = false;
                        try {
                            Log.i("tagconvertstr", "[" + response + "]");
                            JSONObject jsonResponse = new JSONObject(response);
                            boolean success = jsonResponse.getBoolean("success");
                            if (success) {
                                String trainer_name = jsonResponse.getString("trainer_name");
                                authtoken = jsonResponse.getString("token");
                                //String name = jsonResponse.getString("name");

                                //Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
                                //intent.putExtra("name", name);
                                //intent.putExtra("username", username);
                                //LoginActivity.this.startActivity(intent);
                                //Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
                                //LoginActivity.this.startActivity(intent);
                                //startActivityForResult(intent, 1);

                                String accountName = AccountManager.KEY_ACCOUNT_NAME;
                                String accountPassword = password;
                                //final Account account = new Account(email, "com.example.rapid.rapid");

                                if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, true)) {
                                    Log.d("rapid", TAG + "> finishLogin > addAccountExplicitly");
                                    authtoken = AccountManager.KEY_AUTHTOKEN;
                                    String authtokenType = mAuthTokenType;

                                    Account account = rapidAuthenticatorService.GetAccount(ACCOUNT_TYPE);
                                    AccountManager accountManager =
                                            (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
                                    if (accountManager.addAccountExplicitly(account, null, null)) {
                                        // Inform the system that this account supports sync
                                        ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1);
                                        // Inform the system that this account is eligible for auto sync when the network is up
                                        ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true);
                                        // Recommend a schedule for automatic synchronization. The system may modify this based
                                        // on other scheduled syncs and network utilization.
                                        ContentResolver.addPeriodicSync(
                                                account, CONTENT_AUTHORITY, new Bundle(),SYNC_FREQUENCY);
                                        newAccount = true;
                                    }

                                    if (newAccount) {
                                        TriggerRefresh();
                                        PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit()
                                                .putBoolean(PREF_SETUP_COMPLETE, true).commit();

                                    }

                                    Log.d("rapid", TAG + "> ALL SETUP!");
                                    // Creating the account on the device and setting the auth token we got
                                    // (Not setting the auth token will cause another call to the server to authenticate the user)
                                    //mAccountManager.addAccountExplicitly(account, null, null);
                                    //mAccountManager.setAuthToken(account, authtokenType, authtoken);
                                } else {
                                    Log.d("rapid", TAG + "> finishLogin > setPassword");
                                    //mAccountManager.setPassword(account, accountPassword);
                                    Log.d("rapid", TAG + "> done setting account password");
                                }

                                //setAccountAuthenticatorResult(intent.getExtras());
                                //setResult(RESULT_OK, intent);

                                Toast.makeText(getBaseContext(), "Login Successful", Toast.LENGTH_LONG).show();

                                Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
                                intent.putExtra("trainer_name", trainer_name);
                                startActivity(intent);
                            } else {
                                progressDialog.dismiss();
                                onLoginFailed();
                            }

                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                };

                LoginRequest loginRequest = new LoginRequest(email, password, responseListener);
                RequestQueue queue = Volley.newRequestQueue(LoginActivity.this);
                queue.add(loginRequest);


            }
        });

        _loginPasswordInput.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                //_registerPasswordVisibility.setVisibility(s.length() > 0 ? View.VISIBLE : View.GONE);
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                //_registerPasswordVisibility.setVisibility(s.length() > 0 ? View.VISIBLE : View.GONE);
                //_trainerNameWrapper.setBackgroundColor(Color.parseColor("#0000ff"));
            }
        });
        _loginPasswordVisibility.setOnTouchListener(mPasswordVisibleTouchListener);

        _signupLink.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // Start the Signup activity
                Intent intent = new Intent(getApplicationContext(), RegisterActivity.class);
                startActivityForResult(intent, REQUEST_SIGNUP);
            }
        });
    }

    public static void TriggerRefresh() {
        Bundle b = new Bundle();
        // Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
        b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
        b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
        ContentResolver.requestSync(
                rapidAuthenticatorService.GetAccount(ACCOUNT_TYPE), // Sync account
                CONTENT_AUTHORITY,                 // Content authority
                b);                                             // Extras
    }

    private View.OnTouchListener mPasswordVisibleTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            final boolean isOutsideView = event.getX() < 0 ||
                    event.getX() > v.getWidth() ||
                    event.getY() < 0 ||
                    event.getY() > v.getHeight();

            // change input type will reset cursor position, so we want to save it
            final int cursor = _loginPasswordInput.getSelectionStart();

            if (isOutsideView || MotionEvent.ACTION_UP == event.getAction())
                _loginPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT |
                        InputType.TYPE_TEXT_VARIATION_PASSWORD);
            else
                _loginPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT |
                        InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);

            _loginPasswordInput.setSelection(cursor);
            return true;
        }
    };

    /**
     * Show all the accounts registered on the account manager. Request an auth token upon user select.
     *
     * @param authTokenType
     */
    private void showAccountPicker(final String authTokenType, final boolean invalidate) {
        mInvalidate = invalidate;
        final Account availableAccounts[] = mAccountManager.getAccountsByType(AccountGeneral.ACCOUNT_TYPE);

        if (availableAccounts.length == 0) {
            Toast.makeText(this, "No accounts", Toast.LENGTH_SHORT).show();
        } else {
            String name[] = new String[availableAccounts.length];
            for (int i = 0; i < availableAccounts.length; i++) {
                name[i] = availableAccounts[i].name;
            }

            // Account picker
            mAlertDialog = new AlertDialog.Builder(this).setTitle("Pick Account").setAdapter(new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, name), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    if (invalidate)
                        invalidateAuthToken(availableAccounts[which], authTokenType);
                    else
                        getExistingAccountAuthToken(availableAccounts[which], authTokenType);
                }
            }).create();
            mAlertDialog.show();
        }
    }

    /**
     * Get the auth token for an existing account on the AccountManager
     *
     * @param account
     * @param authTokenType
     */
    private void getExistingAccountAuthToken(Account account, String authTokenType) {
        final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, this, null, null);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Bundle bnd = future.getResult();

                    final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
                    showMessage((authtoken != null) ? "SUCCESS!\ntoken: " + authtoken : "FAIL");
                    Log.d("udinic", "GetToken Bundle is " + bnd);
                } catch (Exception e) {
                    e.printStackTrace();
                    showMessage(e.getMessage());
                }
            }
        }).start();
    }

    /**
     * Invalidates the auth token for the account
     *
     * @param account
     * @param authTokenType
     */
    private void invalidateAuthToken(final Account account, String authTokenType) {
        final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, this, null, null);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Bundle bnd = future.getResult();

                    final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
                    mAccountManager.invalidateAuthToken(account.type, authtoken);
                    showMessage(account.name + " invalidated");
                } catch (Exception e) {
                    e.printStackTrace();
                    showMessage(e.getMessage());
                }
            }
        }).start();
    }

    /**
     * Get an auth token for the account.
     * If not exist - add it and then return its auth token.
     * If one exist - return its auth token.
     * If more than one exists - show a picker and return the select account's auth token.
     *
     * @param accountType
     * @param authTokenType
     */
    private void getTokenForAccountCreateIfNeeded(String accountType, String authTokenType) {
        final AccountManagerFuture<Bundle> future = mAccountManager.getAuthTokenByFeatures(accountType, authTokenType, null, this, null, null,
                new AccountManagerCallback<Bundle>() {
                    @Override
                    public void run(AccountManagerFuture<Bundle> future) {
                        Bundle bnd = null;
                        try {
                            bnd = future.getResult();
                            final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
                            showMessage(((authtoken != null) ? "SUCCESS!\ntoken: " + authtoken : "FAIL"));
                            Log.d("udinic", "GetTokenForAccount Bundle is " + bnd);

                        } catch (Exception e) {
                            e.printStackTrace();
                            showMessage(e.getMessage());
                        }
                    }
                }
                , null);
    }

    private void showMessage(final String msg) {
        if (TextUtils.isEmpty(msg))
            return;

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

rapidAuthenticator.java rapidAuthenticator.java

public class rapidAuthenticator extends AbstractAccountAuthenticator {

    private String TAG = "rapidAuthenticator";
    private final Context mContext;

    public rapidAuthenticator(Context context) {
        super(context);

        // I hate you! Google - set mContext as protected!
        this.mContext = context;
    }

    @Override
    public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
        Log.d("rapid", TAG + "> addAccount");

        final Intent intent = new Intent(mContext, LoginActivity.class);
        intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, accountType);
        intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType);
        intent.putExtra(LoginActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);
        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

        final Bundle bundle = new Bundle();
        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
        return bundle;
    }

    @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {

        Log.d("udinic", TAG + "> getAuthToken");

        // If the caller requested an authToken type we don't support, then
        // return an error
        if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) && !authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS)) {
            final Bundle result = new Bundle();
            result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
            return result;
        }

        // Extract the username and password from the Account Manager, and ask
        // the server for an appropriate AuthToken.
        final AccountManager am = AccountManager.get(mContext);

        String authToken = am.peekAuthToken(account, authTokenType);

        Log.d("udinic", TAG + "> peekAuthToken returned - " + authToken);

        // Lets give another try to authenticate the user
        if (TextUtils.isEmpty(authToken)) {
            final String password = am.getPassword(account);
            if (password != null) {
                try {
                    Log.d("udinic", TAG + "> re-authenticating with the existing password");
                    authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        // If we get an authToken - we return it
        if (!TextUtils.isEmpty(authToken)) {
            final Bundle result = new Bundle();
            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
            result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
            return result;
        }

        // If we get here, then we couldn't access the user's password - so we
        // need to re-prompt them for their credentials. We do that by creating
        // an intent to display our AuthenticatorActivity.
        final Intent intent = new Intent(mContext, LoginActivity.class);
        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
        intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, account.type);
        intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType);
        intent.putExtra(LoginActivity.ARG_ACCOUNT_NAME, account.name);
        final Bundle bundle = new Bundle();
        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
        return bundle;
    }


    @Override
    public String getAuthTokenLabel(String authTokenType) {
        if (AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS.equals(authTokenType))
            return AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS_LABEL;
        else if (AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY.equals(authTokenType))
            return AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY_LABEL;
        else
            return authTokenType + " (Label)";
    }

    @Override
    public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
        final Bundle result = new Bundle();
        result.putBoolean(KEY_BOOLEAN_RESULT, false);
        return result;
    }

    @Override
    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
        return null;
    }

    @Override
    public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
        return null;
    }

    @Override
    public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
        return null;
    }
}

Manifest 表现

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rapid.rapid">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />

    <application>
        <service android:name="com.example.rapid.rapid.rapidAuthenticatorService">
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator" />
            </intent-filter>
            <meta-data android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/authenticator" />
        </service>
    </application>
</manifest>

Authenticator.xml Authenticator.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
        android:accountType="com.example.rapid.rapid"
        android:icon="@drawable/logo"
        android:smallIcon="@drawable/logo"
        android:label="rapid"
        android:accountPreferences="@xml/prefs"/>
</selector>

As exception says, caller uid is different than the authenticator's uid . 如异常所示, 呼叫者uid验证 者的uid不同。 To add a account explicitly, caller and authenticator's uid should be same. 要显式添加帐户, 呼叫者验证者的uid应相同。

This should be same as your app id , ie package name . 该名称应与您的应用ID (即包名)相同

android:accountType="com.example.rapid.rapid" 

Android Developer Documentation Android开发人员文档

This method requires the caller to have a signature match with the authenticator that owns the specified account. 此方法要求呼叫者与拥有指定帐户的身份验证者进行签名匹配。

I had a similar problem, but got it fixed after I restarted the device. 我遇到了类似的问题,但是在重新启动设备后将其修复。 It might help to try it out. 尝试一下可能会有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM