68

My code worked in <5 but in Android 5.0 I'm running into an issue that I don't quite understand.

10-23 10:18:18.945: E/AndroidRuntime(8987): java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.google.android.gms.analytics.service.START (has extras) }

My code works, even now, on 4.4.4 and below. So what do I need to do? I'll post relative code below. Also, during my googling I found this post about java.lang.IllegalArgumentException: Service Intent must be explicit in regard to Android 5.0 but I don't understand what it means.

Manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="xxxxx.android.phone.xxxxx"
    android:versionCode="3"
    android:versionName="v1.2.4065" >

    <uses-sdk android:minSdkVersion="12"
        android:targetSdkVersion="21" />

    <!-- Required for Google Analytics -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- For push notifications (GCM) -->
    <permission android:name="xxxxx.android.phone.xxxxx.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="xxxxx.android.phone.xxxxx.permission.C2D_MESSAGE" />
    <!-- App receives GCM messages. -->
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <!-- GCM connects to Google Services. -->
    <uses-permission android:name="android.permission.INTERNET" /> 
    <!-- GCM requires a Google account. -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <!-- Keeps the processor from sleeping when a message is received. -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!-- GCM - We handle notifications differently if the app is running -->
    <uses-permission android:name="android.permission.GET_TASKS" /> 

    <!-- Caching -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <!-- The event subscribe button adds events to the calendar -->
<!--    <uses-permission android:name="android.permission.WRITE_CALENDAR" /> -->
<!--    <uses-permission android:name="android.permission.READ_CALENDAR" />  -->

    <supports-screens
        android:resizeable="true"
        android:smallScreens="false"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:anyDensity="true" />

    <application
        android:name="xxxxx.xxxxxApplication"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name"
        android:allowBackup="true"
        android:largeHeap="true" >
        <receiver 
            android:name="com.google.android.gcm.GCMBroadcastReceiver" 
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="xxxxx.android.phone.xxxxx" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="xxxxx.android.phone.xxxxx" />
            </intent-filter>
        </receiver>

        <receiver 
            android:name="xxxxx.ConnectivityReceiver"
            android:enabled="false" >
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>

        <activity 
            android:name=".SplashActivity"
            android:configChanges="locale|orientation" 
            android:theme="@style/Theme.Splash"
            android:screenOrientation="portrait"
            android:noHistory="true" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:label="@string/app_name"
            android:theme="@style/Theme"
            android:windowSoftInputMode="adjustPan|stateVisible"
            android:name=".LoginActivity"
            android:configChanges="locale|orientation|screenSize" 
            android:screenOrientation="portrait" >
        </activity>
        <activity 
            android:name=".MainActivity" 
            android:theme="@style/Theme"
            android:configChanges="locale|orientation|screenSize" 
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustPan|stateVisible" />

        <activity 
            android:name=".CountryPickerActivity" 
            android:theme="@style/Theme.Floating"
            android:configChanges="locale|orientation|screenSize" 
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustPan|stateVisible" />
        <activity 
            android:name=".EventPickerActivity" 
            android:theme="@style/Theme.Floating"
            android:configChanges="locale|orientation|screenSize" 
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustPan|stateVisible" />
        <activity 
            android:name=".TutorialActivity"
            android:theme="@style/Theme.Transparent"
            android:configChanges="locale|orientation|screenSize"
            android:screenOrientation="portrait" />

        <activity 
            android:name=".VideoPlayerActivity" 
            android:theme="@style/Theme"
            android:configChanges="orientation|screenSize" />

        <service android:name=".GCMIntentService" android:enabled="true" />
        <meta-data android:name="com.crashlytics.ApiKey" android:value="xxxxxxxxxxxxxxxx"/>
    </application>

</manifest>

GCMIntentService.java

public class GCMIntentService extends GCMBaseIntentService {
private static final int ATTEMPTS_MAX = 3;

final static boolean USE_DEV = false;
final static String XXXXX = "https://xxxxx/api.php";
final static String XXXXX = "http://dev.xxxxx/api.php";
final static String SUBSCRIPTION_KEY = "xxxxxxxxxxxxxxx"; // unique per app

    public GCMIntentService() {
        super(xxxxxx.SENDER_ID);
        if(GCMIntentService.USE_DEV) {
            host = XXXXX;
        } else {
            host = XXXXX;
        }
    }

    ...

}

** EDIT **

The more I look at this issue the more I think it's not in GCMIntentService.java. I should have posted my stack trace before which is:

10-23 13:17:08.095: E/AndroidRuntime(10560): FATAL EXCEPTION: GAThread
10-23 13:17:08.095: E/AndroidRuntime(10560): Process: xxxxx.android.phone.xxxxx, PID: 10560
10-23 13:17:08.095: E/AndroidRuntime(10560): java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.google.android.gms.analytics.service.START (has extras) }
10-23 13:17:08.095: E/AndroidRuntime(10560):    at android.app.ContextImpl.validateServiceIntent(ContextImpl.java:1674)
10-23 13:17:08.095: E/AndroidRuntime(10560):    at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1773)
10-23 13:17:08.095: E/AndroidRuntime(10560):    at android.app.ContextImpl.bindService(ContextImpl.java:1751)
10-23 13:17:08.095: E/AndroidRuntime(10560):    at android.content.ContextWrapper.bindService(ContextWrapper.java:538)
10-23 13:17:08.095: E/AndroidRuntime(10560):    at com.google.analytics.tracking.android.AnalyticsGmsCoreClient.connect(AnalyticsGmsCoreClient.java:82)
10-23 13:17:08.095: E/AndroidRuntime(10560):    at com.google.analytics.tracking.android.GAServiceProxy.connectToService(GAServiceProxy.java:279)
10-23 13:17:08.095: E/AndroidRuntime(10560):    at com.google.analytics.tracking.android.GAServiceProxy.createService(GAServiceProxy.java:163)
10-23 13:17:08.095: E/AndroidRuntime(10560):    at com.google.analytics.tracking.android.GAThread.init(GAThread.java:95)
10-23 13:17:08.095: E/AndroidRuntime(10560):    at com.google.analytics.tracking.android.GAThread.run(GAThread.java:493)

So I'm going to try to run GA as explicit intent.

rds
  • 26,253
  • 19
  • 107
  • 134
Jacksonkr
  • 31,583
  • 39
  • 180
  • 284
  • Unfortunately I don't know this area too well but here's an post on reddit in regard to licensing service and explicit intent ; https://www.reddit.com/r/androiddev/comments/2jspqi/got_many_recent_reports_of_license_verification/ . – harism Oct 23 '14 at 16:56

11 Answers11

44

If you're trying to use Google's Licensing mechanism, solution that worked for me:

// explicit Intent, safe
Intent serviceIntent = new Intent(ILicensingService.class.getName());
serviceIntent.setPackage("com.android.vending");
boolean bindResult = mContext.bindService(serviceIntent, this, Context.BIND_AUTO_CREATE);

This is located in com/google/android/vending/licensing/LicenseChecker.java. Do a search for "Base64.decode("

Edit:

Adding reference to Google Licensing java file that has to be patched:

com.google.android.vending.licensing.LicenseChecker.checkAccess(LicenseChecker.java:150)

patch:

new String(
-    Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U="))),
+    Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U=")))
+    .setPackage("com.android.vending"), // this fix the 'IllegalArgumentException: Service Intent must be explicit'
     this, // ServiceConnection.

Source: https://code.google.com/p/android/issues/detail?id=78505#c19

Pascal
  • 15,257
  • 2
  • 52
  • 65
milosmns
  • 3,595
  • 4
  • 36
  • 48
38

Since Android 5.0 (Lollipop) bindService() must always be called with an explicit intent. This was previously a recommendation, but since Lollipop it is enforced: java.lang.IllegalArgumentException: Service Intent must be explicit is thrown every time a call to bindService() is made using implicit intents. The difference between implicit and explicit intents is that the latter specifies the component to start by name (the fully-qualified class name). See the documentation about intent types here.

The issue you are having is due to not having upgraded to a newer version of the google libraries, which comply with Android's restrictions on implicit intents when binding a service on Android 5 Lollipop. To fix the issue, you can upgrade the library to a newer version if available, or update the library code yourself and build your project with the modified version.

If there's no suitable library upgrade in the general case, you need to modify the source code (in your case com.google.analytics.tracking.android.AnalyticsGmsCoreClient.connect()) to call intent.setPackage(packageName) before calling bindService() where intent is the first argument of the bindService() call and packageName is the name of the package containing the service the code is attempting to start (in your case "com.google.android.gms.analytics").

You can use this code as an example how to do this: Unity's updated version of the Google Licensing Library (LVL) which calls bindService with an explicit intent and does not result in IllegalArgumentException.

Yet another way to get around the problem is to rebuild your project and the google libraries using targetSDK no later than 19. This will make it run without crashing on Lollipop but is the less secure option and prevents you from using SDK functionality introduced in later versions (for Android 5).

Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
Maria
  • 633
  • 5
  • 12
21

Migration from Google Analytics v2 to v3 solve the problem for me.

nickkadrov
  • 488
  • 2
  • 12
  • 3
    Hey! I'm using analytics v4 and today I got this exception on lollipop LGE device (Nexus 5). Any pointers for that. I have created a separate file which will be responsible to send screen names and events. TrackerName is App_Tracker. – Ankit Garg Dec 04 '14 at 06:53
  • 1
    I had not tried to use v4, but maybe you have this issue because of other service. Try to turn it off completely. – nickkadrov Dec 05 '14 at 09:31
  • 1
    @AnkitGarg it sounds like we're talking about different software because Google Analytics SDK is still only in the major version of 3. – Jacksonkr Dec 09 '14 at 14:33
  • That is weird, I am getting the same error Google Analytics v3... I guess I will try the latest version. – arne.jans Dec 22 '14 at 16:26
  • 2
    I'm using version v3 but still getting same error in android 5.0 device with targetSdkVersion=21. But I do not get this error with targetSdkVersion=19 in android 5.0 device. I do not understand the reason. What can I do? – realuser Dec 24 '14 at 08:32
  • @nickkadrov I don't see how this answer solves the problem in Android Lollipop. Now, one quite simply has to setPackageName on the Intent to resolve this error. – IgorGanapolsky Mar 02 '15 at 18:25
  • i am using EasyTracker, its crashing. Upgrading from V2 to V3 and change the code from EasyTracker.getInstance().activityStart(this); to EasyTracker.getInstance(this).activityStart(this); worked for me, as it needs to be explicit. – SammyT Apr 30 '15 at 13:10
  • Again, how this can be the answer? It cannot solve the problem. – user1443721 Aug 08 '16 at 01:32
14

I was just having this problem myself. The issue lies within your activity that is starting your service.

Basically, an explicit intent names the service directly in the intent when the service is started. See http://developer.android.com/guide/components/intents-filters.html for a more in-depth explanation.

Since, you don't have your activity code posted, I don't know how you're starting it now, but it should probably look something like this:

Intent startIntent = new Intent(this, ServiceToStart.class);
this.startService(startIntent); // or bindService(...)
Joe Grande
  • 213
  • 1
  • 10
  • Still not sure what's going on. My main activity is huge so should I just post my `onCreate`? That's where the GA service is started. – Jacksonkr Oct 23 '14 at 18:07
  • Sure, just post where the service is started. – Joe Grande Oct 23 '14 at 18:54
  • 1
    Inherent intent was the issue but inside the GA (google analytics) lib. I was planning on upgrading to the new version anyway (3.0) so I just migrated from 2 to 3 and viola. – Jacksonkr Oct 23 '14 at 20:40
  • 4
    This solved it for me. I was using `new Intent(ISomeService.class.getName());`, which works fine in 4.4 but not in 5.0. After switching that line to `new Intent(getApplicationContext(), ISomeService.class);`, it started working again. Note that I was already having a warning about this in 4.4, I just chose to ignore it since I'm only writing a disposable test class. – Daniel Jan 26 '15 at 12:50
  • Small update: the error did go away but now I'm unable to connect to the service. I set my target sdk to 19 until [Google talks to Google](http://commonsware.com/blog/2014/06/29/dealing-deprecations-bindservice.html) and some *fix* pops up. – Daniel Jan 26 '15 at 14:51
  • This fixed my issue, where I changed `startIntent = new Intent(actionString);` to `startIntent = new Intent(this, ServiceToStart.class); startIntent.setAction(actionString);` – kassim Mar 03 '15 at 17:36
  • Thanks! This fixed my problem and possibly saved me many hours of trying to fix broken code! – SJTriggs Apr 14 '15 at 01:01
  • What if I am using GA not on a service but on the constructor of a class that I initialise on a subclass of Activity? – SleepNot Apr 27 '15 at 08:58
8

I used this and it works great

 public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
     //Retrieve all services that can match the given intent
     PackageManager pm = context.getPackageManager();
     List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

     //Make sure only one match was found
       if (resolveInfo == null || resolveInfo.size() != 1) {
        return null;
       }

     //Get component info and create ComponentName
     ResolveInfo serviceInfo = resolveInfo.get(0);
     String packageName = serviceInfo.serviceInfo.packageName;
     String className = serviceInfo.serviceInfo.name;
     ComponentName component = new ComponentName(packageName, className);

     //Create a new intent. Use the old one for extras and such reuse
     Intent explicitIntent = new Intent(implicitIntent);

     //Set the component to be explicit
     explicitIntent.setComponent(component);

     return explicitIntent;
 }
Leebeedev
  • 2,126
  • 22
  • 31
4

I'm working on a project where we want to allow users to use older devices. I came up with the same solution as the one mentioned in Marias answer, however I've added a conditional statement for the setPackage call since it is only available in API 4 (Ice Cream Sandwich == SDK 14) and above. If developing for versions below that I reckon you don't need to include the setPackage call.

In function com.google.android.vending.licensing.LicenseChecker.checkAccess(callback)

Intent serviceIntent = new Intent(new String(
Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U=")));

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    serviceIntent.setPackage("com.android.vending");
}

boolean bindResult =
    mContext.bindService(
        serviceIntent,
        this, // ServiceConnection.
        Context.BIND_AUTO_CREATE);
kumaheiyama
  • 704
  • 1
  • 13
  • 28
2

If you want to start a service which is in another application.you can use this:

Intent serviceIntent = new Intent("action name for the service");
serviceIntent.setPackage("the PackageName for which the service in)");//the destination packageName
context.startService(serviceIntent);
yu xiaofei
  • 55
  • 5
1

This worked for me..This worked in lollipop using android sdk 21..

Intent intent = new Intent(this, Class.forName(ServiceClassName.class.getName()));
bindService(intent,serviceConnection, Service.BIND_AUTO_CREATE);
NorthCat
  • 9,643
  • 16
  • 47
  • 50
Mayur
  • 19
  • 2
1

For PhoneGap/Cordova users who are seeing this error, it's because the semi-official GAPlugin uses the deprecated Google Analytics v2 lib. khalidb91 forked it and updated to v3 which as of this writing hasn't been merged into the semi-official plugin. Grab the code from his fork, drop it in as a direct replacement to plugins/com.adobe.plugins.GAPlugin and no more crashes. Thanks khalidb91!

https://github.com/khalidb91/GAPlugin

pmont
  • 2,083
  • 22
  • 43
0

This works for me:

Intent intent = new Intent(ACTION);
intent.setPackage(context.getPackageName());
context.startService(intent);
researcher
  • 1,758
  • 22
  • 25
-2

If you are trying to start service, try it this way:

var intent = new Intent (this,typeof(MyBoundService));
var serviceConnection = new MyBindServiceConnection (this);
BindService (intent, serviceConnection, Bind.AutoCreate);
Augis
  • 1,903
  • 1
  • 16
  • 21