Push-уведомления C2DM Что является причиной ошибок неавторизации 401? - PullRequest
3 голосов
/ 09 октября 2011

UPDATE

РЕШЕНО - Благодаря постоянству @MusiGenesis, я решил проблему, зарегистрировав новую учетную запись Google и новую учетную запись C2DM. После обновления соответствующих учетных данных на веб-сервере и в приложении для Android все стало работать как по волшебству.

КОНЕЦ ОБНОВЛЕНИЯ

Я ищу полный список причин 401 несанкционированных ошибок при отправке push-уведомления, чтобы я мог попытаться устранить свою проблему.

У меня есть электронная почта Google, зарегистрированная в C2DM Я могу использовать curl для получения кода авторизации

У меня есть токен авторизации от зарегистрированного пользователя в моем приложении для Android

Используя 2 токена авторизации (обновлено), я получаю 401 несанкционированные ошибки с моего веб-сервера при отправке запроса push-уведомлений с моего веб-сервера.

Насколько я могу судить, я делаю все, что мне нужно, поэтому я ищу то, чего мне не хватает. Я покопался в интернете, и у многих людей, похоже, возникла та же самая проблема без однозначного ответа. Любая помощь с благодарностью

UPDATE

Как упомянуто в ответе ниже, кажется, что для получения регистрационного идентификатора требуется второй этап, который отличается от токена авторизации, полученного зарегистрированным пользователем в приложении для Android. Посмотрев на код сноски и эти два ресурса

http://www.vogella.de/articles/AndroidCloudToDeviceMessaging/article.html#implementation_mobileregistration

и

http://marakana.com/forums/android/general/272.html

Я не вижу информации относительно второго регистрационного звонка, чтобы получить регистрационный идентификатор в дополнение к токену авторизации. Я явно что-то упустил и был бы рад, если бы кто-то мог объяснить это для меня.

** ОБНОВЛЕНИЕ 2 **

Мой C2DM-приемник выглядит так

public class C2DMReceiver extends C2DMBaseReceiver {
    public C2DMReceiver() {
        super(REGISTERED_GOOGLE_MAIL_ADDRESS);
    }

    @Override
    public void onRegistered(Context context, String registrationId)
            throws java.io.IOException {
        // The registrationId should be sent to your application server.
        Log.e("C2DM", "Registration ID arrived!");
        Log.e("C2DM", registrationId);
        Intent webSeverReg = new Intent(this, RegService.class);
        startService(webServerReg);     
    };

    @Override
    protected void onMessage(Context context, Intent intent) {
        Log.e("C2DM", "Message: Fantastic!!!");
        // Extract the payload from the message
        Bundle extras = intent.getExtras();
        if (extras != null) {
            System.out.println(extras.get("payload"));
            // Now do something smart based on the information
        }
    }

    @Override
    public void onError(Context context, String errorId) {
        Log.e("C2DM", "Error occured!!!");
    }
}

C2DMBaseReceiver, взятый из приложения jumpnote, выглядит следующим образом

/**
 * Base class for C2D message receiver. Includes constants for the
 * strings used in the protocol.
 */
public abstract class C2DMBaseReceiver extends IntentService {
    private static final String C2DM_RETRY = "com.google.android.c2dm.intent.RETRY";

    public static final String REGISTRATION_CALLBACK_INTENT = "com.google.android.c2dm.intent.REGISTRATION";
    private static final String C2DM_INTENT = "com.google.android.c2dm.intent.RECEIVE";

    // Logging tag
    private static final String TAG = "C2DM";

    // Extras in the registration callback intents.
    public static final String EXTRA_UNREGISTERED = "unregistered";

    public static final String EXTRA_ERROR = "error";

    public static final String EXTRA_REGISTRATION_ID = "registration_id";

    public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
    public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
    public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
    public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
    public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
    public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
    public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";

    // wakelock
    private static final String WAKELOCK_KEY = "C2DM_LIB";

    private static PowerManager.WakeLock mWakeLock;
    private final String senderId;

    /**
     * The C2DMReceiver class must create a no-arg constructor and pass the 
     * sender id to be used for registration.
     */
    public C2DMBaseReceiver(String senderId) {
        // senderId is used as base name for threads, etc.
        super(senderId);
        this.senderId = senderId;
    }

    /**
     * Called when a cloud message has been received.
     */
    protected abstract void onMessage(Context context, Intent intent);

    /**
     * Called on registration error. Override to provide better
     * error messages.
     *  
     * This is called in the context of a Service - no dialog or UI.
     */
    public abstract void onError(Context context, String errorId);

    /**
     * Called when a registration token has been received.
     */
    public void onRegistered(Context context, String registrationId) throws IOException {
        // registrationId will also be saved
    }

    /**
     * Called when the device has been unregistered.
     */
    public void onUnregistered(Context context) {
    }


    @Override
    public final void onHandleIntent(Intent intent) {
        Log.d(TAG, "@@@@ - onHandleIntent Messaging request received");
        try {
            Context context = getApplicationContext();
            if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
                handleRegistration(context, intent);
            } else if (intent.getAction().equals(C2DM_INTENT)) {
                onMessage(context, intent);
            } else if (intent.getAction().equals(C2DM_RETRY)) {
                C2DMessaging.register(context, senderId);
            }
        } finally {
            //  Release the power lock, so phone can get back to sleep.
            // The lock is reference counted by default, so multiple 
            // messages are ok.

            // If the onMessage() needs to spawn a thread or do something else,
            // it should use it's own lock.
            mWakeLock.release();
        }
    }


    /**
     * Called from the broadcast receiver. 
     * Will process the received intent, call handleMessage(), registered(), etc.
     * in background threads, with a wake lock, while keeping the service 
     * alive. 
     */
    static void runIntentInService(Context context, Intent intent) {
        if (mWakeLock == null) {
            // This is called from BroadcastReceiver, there is no init.
            PowerManager pm = 
                (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 
                    WAKELOCK_KEY);
        }
        mWakeLock.acquire();

        // Use a naming convention, similar with how permissions and intents are 
        // used. Alternatives are introspection or an ugly use of statics. 
        String receiver = context.getPackageName() + ".C2DMReceiver";
        intent.setClassName(context, receiver);

        context.startService(intent);

    }


    private void handleRegistration(final Context context, Intent intent) {
        final String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID);
        Log.d(TAG, "@@@@ - HandleRegistration Messaging request received");
        String error = intent.getStringExtra(EXTRA_ERROR);
        String removed = intent.getStringExtra(EXTRA_UNREGISTERED);

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "dmControl: registrationId = " + registrationId +
                ", error = " + error + ", removed = " + removed);
        }

        if (removed != null) {
            // Remember we are unregistered
            C2DMessaging.clearRegistrationId(context);
            onUnregistered(context);
            return;
        } else if (error != null) {
            // we are not registered, can try again
            C2DMessaging.clearRegistrationId(context);
            // Registration failed
            Log.e(TAG, "Registration error " + error);
            onError(context, error);
            if ("SERVICE_NOT_AVAILABLE".equals(error)) {
                long backoffTimeMs = C2DMessaging.getBackoff(context);

                Log.d(TAG, "Scheduling registration retry, backoff = " + backoffTimeMs);
                Intent retryIntent = new Intent(C2DM_RETRY);
                PendingIntent retryPIntent = PendingIntent.getBroadcast(context, 
                        0 /*requestCode*/, retryIntent, 0 /*flags*/);

                AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
                am.set(AlarmManager.ELAPSED_REALTIME,
                        backoffTimeMs, retryPIntent);

                // Next retry should wait longer.
                backoffTimeMs *= 2;
                C2DMessaging.setBackoff(context, backoffTimeMs);
            } 
        } else {
            try {
                onRegistered(context, registrationId);
                C2DMessaging.setRegistrationId(context, registrationId);
            } catch (IOException ex) {
                Log.e(TAG, "Registration error " + ex.getMessage());
            }
        }
    }
}

1 Ответ

3 голосов
/ 09 октября 2011

Вы говорите, что у вас "есть токен авторизации от зарегистрированного пользователя в моем приложении для Android". Возможно, вы только что написали это неправильно, но если вы имеете в виду буквально, что вы используете токен зарегистрированного пользователя , а не идентификатор регистрации , полученный пользователем с сервера C2DM, то тут твоя проблема.

Редактировать: Ваше клиентское приложение (работающее на устройстве) использует трехэтапный процесс для C2DM: 1) вызвать сервер C2DM, передавая идентификатор и пароль учетной записи gmail клиента, вернуть токен авторизации; 2) снова вызвать C2DM-сервер, используя токен аутентификации, начиная с шага 1, вернуть регистрационный идентификатор (который составляет 96-120 символов ASCII splooge); 3) вызовите приложение своего сервера и передайте регистрационный идентификатор , полученный на шаге 2 (а не токен аутентификации , полученный на шаге 1).

Когда ваше серверное приложение затем хочет что-то отправить клиенту, оно обращается к серверу C2DM для получения токена аутентификации (передавая электронную почту и пароль, которые вы использовали для регистрации на сервере C2DM, не адрес электронной почты и пароль пользователя клиента), затем использует этот токен аутентификации вместе с регистрационным идентификатором клиента для выполнения отправки.

Редактировать 2: мое описание того, что происходит на клиенте, здесь неверно - код клиента не предполагает получение токена oauth в любой момент. Все это обрабатывается самой ОС Android. Этот урок:

http://www.vogella.de/articles/AndroidCloudToDeviceMessaging/article.html

хорошо показывает, как все работает для C2DM.

Редактировать 3: Самая распространенная ошибка, с которой я столкнулся в C2DM, связана с использованием в документации фразы «электронная почта отправителя». Этот термин относится к учетной записи gmail, которая была «зарегистрирована» для использования с C2DM, и не относится к учетной записи gmail пользователя Android. Эта учетная запись gmail используется приложением вашего веб-сервера (вместе с соответствующим паролем) для получения oauth-токена из C2DM. Эта же учетная запись должна использоваться клиентским приложением Android (без соответствующего пароля, который он не знает), чтобы выполнить вызов, который возвращает регистрационный идентификатор, который затем отправляется на ваш веб-сервер.

...