无法向 GCM 注册 Android 应用程序:不允许启动服务意图

Unable to Register Android App with GCM : Not allowed to start service Intent

使用 Android Studio 构建应用程序。 应用程序配置中存在一些问题,会引发 java.lang.SecurityException.

项目存储在https://github.com/snambi/gcm_register

AndroidManifest.xml

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />

    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.USE_CREDENTIALS"/>
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>

    <permission android:name="org.antennae.gcmtests.gcmtest.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
    <uses-permission android:name="org.antennae.gcmtests.gcmtest.permission.C2D_MESSAGE"/>


    <activity
        android:name=".MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service android:name=".MyGcmIntentService" android:exported="false" ></service>
</application>

MainActivity

public class MainActivity extends ActionBarActivity {

public static final String EXTRA_MESSAGE = "message";
private static final String PROPERTY_APP_VERSION = "appVersion";
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;

Context context;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    context = getApplicationContext();
    // GcmWrapper startup

    GcmWrapper gcmwrapper = new GcmWrapper(context);
    String registrationId = gcmwrapper.getRegId();

    Toast.makeText(context, "REG_ID = "+ registrationId, Toast.LENGTH_SHORT).show();
 }
}

使用 GCM ( GcmWrapper ) 注册的代码

public class GcmWrapper {

private Context context;

public GcmWrapper( Context context){
    this.context = context;
}

public String getRegId(){
    String regId = getRegIdFromPrefs();
    if( regId == null || regId.trim().equals("") ){
        registerWithGcm();
    }

    return regId;
}

public String getRegIdFromPrefs(){

    String registrationId=null;

    SharedPreferences preferences = context.getSharedPreferences(Constants.PREF_ANTENNAE, Context.MODE_PRIVATE);
    registrationId  = preferences.getString(Constants.PREF_REGISTRATION_ID, "");

    int appVersion = AppVersionUtils.getAppVersion(context);
    SharedPreferences prefs =PreferencesUtil.getSharedPreferences(context);
    if( appVersion != AppVersionUtils.getSavedAppVersion( prefs ) ){
        // if different, clear the saved regId
        registrationId=null;
    }

    return registrationId;
}

public void registerWithGcm(){
    GcmRegistrationTask registerTask = new GcmRegistrationTask(context, Constants.PROJECT_ID);
    registerTask.execute();
}

public static class GcmRegistrationTask extends AsyncTask{

    private Context context;
    private String projectId;

    public GcmRegistrationTask( Context context, String projectId ){
        this.context = context;
        this.projectId = projectId;
    }

    @Override
    protected Object doInBackground(Object[] params) {
        String regId = null;

        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
        try {
            regId = gcm.register( projectId );
        } catch (IOException e) {
            e.printStackTrace();
        }

        return regId;
    }
  }
}

抛出异常

06-18 22:54:01.737  24266-24317/org.antennae.gcmtests.gcmtest E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
java.lang.RuntimeException: An error occured while executing doInBackground()
        at android.os.AsyncTask.done(AsyncTask.java:299)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
        at java.util.concurrent.FutureTask.run(FutureTask.java:239)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
        at java.lang.Thread.run(Thread.java:841)
 Caused by: java.lang.SecurityException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.REGISTER pkg=com.google.android.gms (has extras) } without permission com.google.android.c2dm.permission.RECEIVE
        at android.app.ContextImpl.startServiceAsUser(ContextImpl.java:1412)
        at android.app.ContextImpl.startService(ContextImpl.java:1394)
        at android.content.ContextWrapper.startService(ContextWrapper.java:473)
        at com.google.android.gms.gcm.GoogleCloudMessaging.zzx(Unknown Source)
        at com.google.android.gms.gcm.GoogleCloudMessaging.register(Unknown Source)
        at org.antennea.gcm.GcmWrapper$GcmRegistrationTask.doInBackground(GcmWrapper.java:70)
        at android.os.AsyncTask.call(AsyncTask.java:287)
        at java.util.concurrent.FutureTask.run(FutureTask.java:234)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
        at java.lang.Thread.run(Thread.java:841)

看起来这对其他人来说也是一个问题(参见 here)。

我检查了官方 GCM 文档,确实可以验证您需要将 uses-permission 放在应用程序标签之外。

顺便说一下,您似乎在使用已弃用的 C2DM 库。您可能希望使用 latest one 来避免以后迁移的麻烦。文档中也提供了一个示例项目。