title: SERVICE_NOT_AVAILABLE error while using c2dm
body: <p>I am trying to get C2DM to work, I'm following the ChrometoPhone example <a href="http://code.google.com/p/chrometophone/source/browse/" rel="nofollow">here</a>
  When my emulator tries to register to C2DM, I get <code>SERVICE_NOT_AVAILABLE</code>. I have checked that the emulator has a gmail account set up and the same account is being passed as <code>senderId</code> from <code>DeviceRegistrar.java</code> in the example code. I am also sure that my emulator can access the Internet and I have all permissions (to access the Internet, wake lock etc.). What else can be wrong here?</p>
  
  <p>I'm not sure if I did a good job of explaining the problem. Please let me know if I need to explain something else. </p>
  
  <p>Here is <code>C2DMBaseReceiver.java</code></p>
  
  <pre><code>package com.apps.terrapin;
  
  import java.io.IOException;
  
  import android.app.AlarmManager;
  import android.app.IntentService;
  import android.app.PendingIntent;
  import android.content.Context;
  import android.content.Intent;
  import android.os.PowerManager;
  import android.util.Log;
  
  /**
   * 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) {
          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);
          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());
              }
          }
      }
  }
  </code></pre>
  
  <p>C2DMBroadcastReceiver.java</p>
  
  <pre><code>package com.apps.terrapin;
  
  import android.app.Activity;
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.util.Log;
  
  /**
   * Helper class to handle BroadcastReciver behavior.
   * - can only run for a limited amount of time - it must start a real service 
   * for longer activity
   * - must get the power lock, must make sure it's released when all done.
   * 
   */
  public class C2DMBroadcastReceiver extends BroadcastReceiver {
  
      @Override
      public final void onReceive(Context context, Intent intent) {
          // To keep things in one place.
          C2DMBaseReceiver.runIntentInService(context, intent);
          setResult(Activity.RESULT_OK, null /* data */, null /* extra */);
  
          if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
              Log.e("TAG", "Broadcast receiver got REGISTRATION");
              Log.e("TAG", "ID: " + intent.getExtras().getString("registration_id"));
  
          } else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
              Log.e("TAG", "Broadcast receiver got message");
           }
      }
  }
  </code></pre>
  
  <p>C2DMessaging.java</p>
  
  <pre><code>package com.apps.terrapin;
  
  /*
   * Copyright 2010 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.
   */
  
  import android.app.PendingIntent;
  import android.content.Context;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.content.SharedPreferences.Editor;
  
  /**
   * Utilities for device registration.
   *
   * Will keep track of the registration token in a private preference.
   */
  public class C2DMessaging {
      public static final String EXTRA_SENDER = "sender";
      public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
      public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
      public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
      public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
      public static final String BACKOFF = "backoff";
      public static final String GSF_PACKAGE = "com.google.android.gsf";
  
  
      // package
      static final String PREFERENCE = "com.google.android.c2dm";
  
      private static final long DEFAULT_BACKOFF = 30000;
  
      /**
       * Initiate c2d messaging registration for the current application
       */
      public static void register(Context context, String senderId) {
          Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
          //registrationIntent.setPackage(GSF_PACKAGE);
          registrationIntent.putExtra("app", PendingIntent.getBroadcast(context, 0, new Intent(), 0));
          registrationIntent.putExtra("sender", senderId);
          context.startService(registrationIntent);
      }
  
      /**
       * Unregister the application. New messages will be blocked by server.
       */
      public static void unregister(Context context) {
          Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
          regIntent.setPackage(GSF_PACKAGE);
          regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context,
                  0, new Intent(), 0));
          context.startService(regIntent);
      }
  
      /**
       * Return the current registration id.
       *
       * If result is empty, the registration has failed.
       *
       * @return registration id, or empty string if the registration is not complete.
       */
      public static String getRegistrationId(Context context) {
          final SharedPreferences prefs = context.getSharedPreferences(
                  PREFERENCE,
                  Context.MODE_PRIVATE);
          String registrationId = prefs.getString("dm_registration", "");
          return registrationId;
      }
  
      public static long getLastRegistrationChange(Context context) {
          final SharedPreferences prefs = context.getSharedPreferences(
                  PREFERENCE,
                  Context.MODE_PRIVATE);
          return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
      }
  
      static long getBackoff(Context context) {
          final SharedPreferences prefs = context.getSharedPreferences(
                  PREFERENCE,
                  Context.MODE_PRIVATE);
          return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
      }
  
      static void setBackoff(Context context, long backoff) {
          final SharedPreferences prefs = context.getSharedPreferences(
                  PREFERENCE,
                  Context.MODE_PRIVATE);
          Editor editor = prefs.edit();
          editor.putLong(BACKOFF, backoff);
          editor.commit();
  
      }
  
      // package
      static void clearRegistrationId(Context context) {
          final SharedPreferences prefs = context.getSharedPreferences(
                  PREFERENCE,
                  Context.MODE_PRIVATE);
          Editor editor = prefs.edit();
          editor.putString("dm_registration", "");
          editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
          editor.commit();
  
      }
  
      // package
      static void setRegistrationId(Context context, String registrationId) {
          final SharedPreferences prefs = context.getSharedPreferences(
                  PREFERENCE,
                  Context.MODE_PRIVATE);
          Editor editor = prefs.edit();
          editor.putString("dm_registration", registrationId);
          editor.commit();
  
      }
  }
  </code></pre>
  
  <p>C2DMReceiver.java</p>
  
  <pre><code>package com.apps.terrapin;
  
  /*
   * Copyright 2010 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.
   */
  
  import android.content.Context;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.os.Bundle;
  import android.util.Log;
  
  public class C2DMReceiver extends C2DMBaseReceiver {
      public C2DMReceiver() {
          super(DeviceRegistrar.SENDER_ID);
      }
  
      @Override
      public void onRegistered(Context context, String registration) {
          DeviceRegistrar.registerWithServer(context, registration);
      }
  
      @Override
      public void onUnregistered(Context context) {
          SharedPreferences prefs = Prefs.get(context);
          String deviceRegistrationID = prefs.getString("deviceRegistrationID",
                  null);
          DeviceRegistrar.unregisterWithServer(context, deviceRegistrationID);
      }
  
      @Override
      public void onError(Context context, String errorId) {
          Log.e("TAG", "Error ocurred in onError");
      }
  
      @Override
      public void onMessage(Context context, Intent intent) {
          Bundle extras = intent.getExtras();
          String msg = extras.getString("message");
  
          Log.e("TAG", "Got a message from cloud: " + msg);
      }
  }
  </code></pre>
  
  <p>DeviceRegistrar.java</p>
  
  <pre><code>package com.apps.terrapin;
  
  import java.io.IOException;
  import java.net.URI;
  import java.net.URISyntaxException;
  
  import org.apache.http.HttpResponse;
  import org.apache.http.client.ClientProtocolException;
  import org.apache.http.client.HttpClient;
  import org.apache.http.client.methods.HttpGet;
  import org.apache.http.impl.client.DefaultHttpClient;
  
  import android.content.Context;
  import android.util.Log;
  
  public class DeviceRegistrar {
      static final String SENDER_ID = "sender@gmail.com";
  
      public static void registerWithServer(final Context context, final String deviceRegistrationID) {
          HttpClient client = new DefaultHttpClient();
          HttpGet request = new HttpGet();
  
          String url = "http://" + "192.168.1.11" + "/message.php?";
          url += "device=" + deviceRegistrationID;
  
          try {
              request.setURI(new URI(url));
              HttpResponse response = client.execute(request);
              Log.d("@@@@@ Server response @@@@@", response.toString());
          } catch (URISyntaxException e) {
              e.printStackTrace();
          } catch (ClientProtocolException e) {
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  
      public static void unregisterWithServer(final Context context, final String deviceRegistrationID) {
  
      }
  }
  </code></pre>
  
  <p>Manifest:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:installLocation="preferExternal"
      package="com.apps.terrapin"
      android:versionCode="1"
      android:versionName="1.0" &gt;
      &lt;!-- Only this application can receive the messages and registration result --&gt;    
      &lt;permission android:name="com.apps.terrapin.permission.C2D_MESSAGE" 
          android:protectionLevel="signature" /&gt;
      &lt;uses-permission android:name="com.apps.terrapin.permission.C2D_MESSAGE" /&gt;
      &lt;!-- This app has permission to register and receive data message --&gt;
      &lt;uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /&gt;
  
      &lt;!-- Permissions for internet access and account access --&gt;
      &lt;uses-permission android:name="android.permission.INTERNET" /&gt;
  
      &lt;!-- App must have this permission to use the library --&gt;
      &lt;uses-permission android:name="android.permission.WAKE_LOCK" /&gt;
  
      &lt;application
          android:icon="@drawable/earth"
          android:label="@string/app_name" &gt;
          &lt;activity
              android:label="@string/app_name"
              android:name=".TerraPin" &gt;
              &lt;intent-filter &gt;
                  &lt;action android:name="android.intent.action.MAIN" /&gt;
  
                  &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
              &lt;/intent-filter&gt;
          &lt;/activity&gt;
          &lt;activity android:name=".TakePictureActivity" /&gt;
          &lt;activity android:name=".SearchActivity" /&gt;
          &lt;activity android:name=".SettingsActivity" /&gt;
  
          &lt;uses-library android:name="com.google.android.maps" /&gt;
  
          &lt;service android:name=".C2DMReceiver" /&gt;
  
          &lt;!-- Only google service can send data messages for the app. If permission is not set -
               any other app can generate it --&gt; 
          &lt;receiver android:name="com.apps.terrapin.C2DMBroadcastReceiver"
                    android:permission="com.google.android.c2dm.permission.SEND"&gt;
              &lt;!-- Receive the actual message --&gt;
              &lt;intent-filter&gt;
                  &lt;action android:name="com.google.android.c2dm.intent.RECEIVE" /&gt;
                  &lt;category android:name="com.apps.terrapin" /&gt;
              &lt;/intent-filter&gt;
              &lt;!-- Receive the registration id --&gt;
              &lt;intent-filter&gt;
                  &lt;action android:name="com.google.android.c2dm.intent.REGISTRATION" /&gt;
                  &lt;category android:name="com.apps.terrapin" /&gt;
              &lt;/intent-filter&gt;
          &lt;/receiver&gt;
  
      &lt;/application&gt;
  
      &lt;uses-sdk android:minSdkVersion="10" /&gt;
  
      &lt;uses-permission android:name="android.permission.CAMERA" /&gt;
      &lt;uses-permission android:name="android.permission.INTERNET" /&gt;
      &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /&gt;
      &lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /&gt;
      &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
      &lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /&gt;
      &lt;uses-permission android:name="android.permission.WAKE_LOCK" /&gt;
  
  &lt;/manifest&gt;
  </code></pre>
  
  <p>The error returned is <code>SERVICE_NOT_AVAILABLE</code> </p>
  
  <p>In my main activity, I am doing this:</p>
  
  <pre><code>C2DMessaging.register(this, DeviceRegistrar.SENDER_ID);
          String regId = C2DMessaging.getRegistrationId(this);
          if (regId != null &amp;&amp; !"".equals(regId)) {
              DeviceRegistrar.registerWithServer(this, regId);
          } else {
              C2DMessaging.register(this, DeviceRegistrar.SENDER_ID);
          }
  </code></pre>