IInAppBillingService Object is null - android

I'm trying to use Android In App Billing by following google documents. But I tried to use bindService method for using InAppBillingService Object (mService). It returns true, but mService is still null. here is my code
public class PaymentActivity extends AppCompatActivity {
IInAppBillingService mService;
ServiceConnection mServiceConn;
ArrayList<String> skuList;
Bundle querySkus;
Bundle skuDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_payment);
Log.d("payment", "isBillingAvailable? " + isBillingAvailable(this));
String chargeString = getIntent().getStringExtra("charge");
Log.d("intentTest", "charge is: " + chargeString);
mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d("Payment", "service disconnected!");
mService = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
Log.d("Payment", "service connected!");
}
};
}
#Override
protected void onStart() {
super.onStart();
// Bind to IInAppBillingService
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
this.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
try{
while(mService == null){
Thread.sleep(1000);
Log.d("payment", "sleep 1 second");
}
}catch (InterruptedException e)
{
e.printStackTrace();
}
skuList = new ArrayList<String> ();
skuList.add("premiumUpgrade");
skuList.add("gas");
querySkus = new Bundle();
querySkus.putStringArrayList("ITEM_ID_LIST", skuList);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
unbindService(mServiceConn);
}
}
public static boolean isBillingAvailable(Context context) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
List<ResolveInfo> list = packageManager.queryIntentServices(intent, 0);
return list.size() > 0;
}
}
I know it is vert bad code because bindService is called in onCreate. But I tried it on Asyn and there was no change. I tried to wait until connection is completed by sleeping mainTread. This try makes my App in infinited Loop.
my error message is
java.lang.RuntimeException: Unable to start activity ComponentInfo{kr.co.bigsapp.www/kr.co.bigsapp.www.activities.PaymentActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'android.os.Bundle com.android.vending.billing.IInAppBillingService.getSkuDetails(int, java.lang.String, java.lang.String, android.os.Bundle)' on a null object reference
Please help me TT

Your approach (looping until mService is non-null) is flawed. In fact, that's what's preventing the service from being created.
The creation of a local, "in-process" Service does not happen synchronously with the bindService()/startService() call. You have to return control to the framework; in this case, that means returning from onStart(). This is because the state of the Service is advanced by the message queue (Looper) that is running on the main thread, and while you're stuck in onStart(), that Looper isn't "looping".
If you return, you'll find that Service.onCreate() and ServiceConnection.onServiceConnected() are called within a few milliseconds.

Related

Android Service starting 2 times because of BindService and StartService

I've been working on a music player app. I'm using a service to run the MediaPlayer. From a fragment I start the service using startService(Intent) and then I bound it to my activity. At least that's what I intend to do. The thing is that my app after getting terminated attempts to launch the service again and since the app is already terminated, the service throws an exception.
E/ActivityThread: Activity com.veloxigami.myapplication.MainActivity has leaked ServiceConnection com.veloxigami.myapplication.MainFragment$1#d8b488c that was originally bound here
android.app.ServiceConnectionLeaked: Activity com.veloxigami.myapplication.MainActivity has leaked ServiceConnection com.veloxigami.myapplication.MainFragment$1#d8b488c that was originally bound here.
My onStartCommand() is getting called 2 times. Although I've been able to stop the crashing message by returning START_NOT_STICKY in onStartCommand() as it was suggested in this link. I would like to understand what is the actual problem here.
My project is available on my GitHub if anyone would like to check the code. Music-Player-App.
I'm using a fragment in my MainActivity to work with the service. Below codes are where I work in between MainFragment and MediaPlayerService.
MainFragment
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
playerService = binder.getService();
serviceBound = true;
Toast.makeText(getActivity(), "Media Player Active", Toast.LENGTH_SHORT).show();
}
#Override
public void onServiceDisconnected(ComponentName name) {
serviceBound = false;
}
};
public void playAudio(int audioIndex) {
currentFile = audioIndex;
if (!serviceBound) {
// storage = new DataStorage(getActivity());
/* storage.storeAudio(playlist);
storage.storeAudioIndex(audioIndex);*/
serviceBound = true;
Log.v("TAG", "Creating new instance");
Intent playerIntent = new Intent(getActivity(), MediaPlayerService.class);
getActivity().startService(playerIntent);
getActivity().bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
//storage = new DataStorage(getActivity());
/*storage.storeAudio(playlist);
storage.storeAudioIndex(audioIndex);*/
Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
Log.v("TAG", "Broadcasting");
getActivity().sendBroadcast(broadcastIntent);
}
Intent playingBroadcast = new Intent(Broadcast_PLAY_BTN_CHANGE);
getActivity().sendBroadcast(playingBroadcast);
Intent nextPlayingBroadcastMain = new Intent(Broadcast_SONG_TEXT_CHANGE);
getActivity().sendBroadcast(nextPlayingBroadcastMain);
}
MediaPlayerService
private void initMediaPlayer(){
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try{
mediaPlayer.setDataSource(currentMedia.getData());
currentFileIndex = MainFragment.currentFile;
MainActivity.durationText.setText(currentMedia.getDuration());
Toast.makeText(getApplicationContext(),"Playlist Size: "+MainFragment.playlist.size() +"\nSong No.: "+(currentFileIndex+1) ,Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
stopSelf();
}
mediaPlayer.prepareAsync();
}
#Override
public void onCreate() {
super.onCreate();
callStateListener();
registerAudioOutputChange();
register_playNewAudio();
registerStopMediaBroadcast();
registerUpdatePlaylistReceiver();
registerPlayButtonBroadcast();
registerPrevButtonBroadcast();
registerNextButtonBroadcast();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
playList = new ArrayList<>();
playList = MainFragment.playlist;
currentMedia = MainFragment.playlist.get(MainFragment.currentFile);
}catch (NullPointerException e){
e.printStackTrace();
stopSelf();
}
if(requestAudioFocus() == false)
stopSelf();
if (currentMedia.getData() != null && currentMedia.getData() !="") {
initMediaPlayer();
}
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer!=null){
stopMedia();
mediaPlayer.release();
}
removeAudioFocus();
if(phoneStateListener != null){
telephonyManager.listen(phoneStateListener,PhoneStateListener.LISTEN_NONE);
}
unregisterReceiver(audioOutputChange);
unregisterReceiver(playNewAudio);
unregisterReceiver(stopMediaBroadcast);
unregisterReceiver(updatePlaylistReceiver);
unregisterReceiver(playButtonBroadcast);
unregisterReceiver(prevButtonBroadcast);
unregisterReceiver(nextButtonBroadcast);
//new DataStorage(getApplicationContext()).clearCachedAudioPlaylist();
}
You don't have an unbindService call anywhere in your code. So whenever the Activity gets destroyed, the system detects that it is still bound to a ServiceConnection and has been leaked. This is still the case when calling bindService inside of a Fragment. Since fragments don't inherit from Activity or Context, they don't have a context reference themselves thus they must use their parent Activities context. Remember to always call unbindService when the owning component is being destroyed, whether it's a Fragment, Activity, or even another Service. It's not unheard for a service to bind to another.
If you don't want your bound service to be destroyed when all clients unbind, you need to add special logic to determine if the Service should transition to a started service temporarily so it won't be killed by the OS, and stop the service when a client rebinds to it.

Not binding in-app billing service from IabHelper

I want to include in-app purchases to my app, but I cannot bind my activity to the in-app billing service.
I have already done all the steps mentioned in the page https://developer.android.com/training/in-app-billing/preparing-iab-app.html
Debugging in a physical device I found that the issue is in the next command of the IabHelper class:
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
I noticed that it is not working because the program does not stop at either of the two methods of the ServiceConnection instance, that is to say, it does not stop at onServiceConnected() nor onServiceDisconnected()
I made a test using the same command directly in my Activity and the bind with the in-app billing service was successful.
So, the bind is working if it is requested from the Activity but it is not working when it is requested from the IabHelper class.
My question is, how can I bind my activity to the billing service from the IabHelper class?
Here is the code to call the startSetup method from IabHelper:
mHelper = new IabHelper(this, publicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
if (mHelper == null) return;
}
});
This is the code within IabHelper class to bind to service:
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
List<ResolveInfo> intentServices = mContext.getPackageManager().queryIntentServices(serviceIntent, 0);
if (intentServices != null && !intentServices.isEmpty())
{
// service available to handle that Intent
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
}
And here is the code that I used to bind to the service directly from the activity:
private IInAppBillingService mService;
ServiceConnection mServiceConn = new ServiceConnection()
{
#Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
}
#Override
public void onServiceConnected(ComponentName name,IBinder service)
{
mService = IInAppBillingService.Stub.asInterface(service);
}
};
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
this.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
Thanks in advance for your help
I just realize my mistake, I was calling the queryInventoryAsync method before the startSetup get finished.
This was my mistake:
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
}
});
try {mHlpr.queryInventoryAsync(true, itemList, mQueryListener);}
catch (IabHelper.IabAsyncInProgressException e) {e.printStackTrace();}
To correct, I changed the code to:
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
else if(result.isSuccess())
{
try {mHlpr.queryInventoryAsync(true, itemList, mQueryListener);}
catch (IabHelper.IabAsyncInProgressException e) {e.printStackTrace();}
}
}
});
When I tested the bind directly from the activity, I had removed the command for the query, this is the reason it worked from the activity; when I was testing from the IabHelper class I was calling the query command

Google Play Service Activity recognition start and stops itself constantly

I need to call the Google activity recognition service through a service (not activity) and run it in the background, of course when the user starts the app, which has an activity (But the service does not called directly from activity).
Therefore I have created a service class (ActivitySensor) and another class (ActivityRecognitionScan).
When I install the app on my Galaxy Nexus S device, the service starts calling onCreate and onDestroy automatically. Even without doing anything in the GUI
It is very strange behaviour. Does anybody has the same experience or solution for it?
I mean I get something as follows in the debug console:
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
...
Here are my two classes:
public class ActivitySensor extends IntentService {
private ActivityRecognitionScan myascan;
private Intent inIntent;
private static long ACTIVITY_LOG_INTERVAL = 30000L;
private static JsonEncodeDecode jsonencoder = new JsonEncodeDecode();
public ActivitySensor() {
super("ActivitySensor");
}
#Override
public void onCreate(){
super.onCreate();
Log.d("Activity-Logging", "--- onCreate");
try {
myascan = new ActivityRecognitionScan(getApplicationContext());
myascan.startActivityRecognitionScan();
} catch (Exception e) {
Log.e("[Activity-Logging]","----------Error:"+e.getLocalizedMessage());
e.printStackTrace();
}
}
#Override
public void readSensor() {
// Log.e("Activity-Logging", "ActivityRecognitionResult.hasResult: "+String.valueOf(ActivityRecognitionResult.hasResult(inIntent)));
if (ActivityRecognitionResult.hasResult(inIntent)) {
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(inIntent);
DetectedActivity activity = result.getMostProbableActivity();
final int type = activity.getType();
String strType = new String();
switch(type){
case DetectedActivity.IN_VEHICLE:
strType = "invehicle";
break;
case DetectedActivity.ON_BICYCLE:
strType ="onbicycle";
break;
case DetectedActivity.ON_FOOT:
strType = "onfoot";
break;
case DetectedActivity.STILL:
strType = "still";
break;
case DetectedActivity.TILTING:
strType ="tilting";
break;
case DetectedActivity.UNKNOWN:
strType ="unknown";
break;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Editor edt = prefs.edit();
String previousActv = prefs.getString("PREVIOUS_ACTIVIY","");
long previousDate = prefs.getLong("PREVIOUS_DATE", 0);
if (previousActv.length()==0){ // nothing was in the string and it is the first time just initialize
previousActv = strType;
previousDate = new Date().getTime();
// Log.e("-----FIRST TIME: type:", previousActv+" date:"+String.valueOf(previousDate));
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", previousDate);
edt.commit();
}else {
if (!strType.equalsIgnoreCase(previousActv)){
Date readablePrevDate = new Date(previousDate);
Date nowDate = new Date();
String jsonstr = jsonencoder.EncodeActivity("Activity", readablePrevDate, nowDate, strType, activity.getConfidence());
// Log.e("[Activity-Logging] ----->",jsonstr);
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", nowDate.getTime());
edt.commit();
DataAcquisitor.dataBuff.add(jsonstr);
}
}
}
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Activity-Logging", "--- onHandleIntent"+ "---"+intent.getAction());
intent.putExtra("LOG_INTERVAL",ACTIVITY_LOG_INTERVAL );
intent.putExtra("STOP",false);
inIntent = intent;
readSensor();
}
#Override
public void onDestroy(){
Log.d("Activity-Logging", "--- onDestroy");
myascan.stopActivityRecognitionScan();
myascan=null;
//super.onDestroy();
}
}
This is the class that calls the Google Activity Recognition Service:
ActivityRecognitionScan implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener {
private Context ctx;
private static final String TAG = "ActivityRecognition";
private static ActivityRecognitionClient actrecClient;
private static PendingIntent callbackIntent;
private long ACTIVITY_LOG_INTERVAL=30000;
public ActivityRecognitionScan(Context context) {
ctx=context;
}
public void startActivityRecognitionScan(){
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ctx);
if(resp == ConnectionResult.SUCCESS){
actrecClient = new ActivityRecognitionClient(ctx, this, this);
if (!actrecClient.isConnected()){
actrecClient.connect();
} else{
Log.e("ActivityRecognitionScan"," ---Activity recognition client is already connected");
}
}else{
Log.e("[Activity-Logging]", "Google Play Service hasn't installed");
}
}
public void stopActivityRecognitionScan(){
try{
if (actrecClient.isConnected() || actrecClient.isConnecting() ){
actrecClient.removeActivityUpdates(callbackIntent);
actrecClient.disconnect();
}
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.e("[ActivityRecognitionScan]", "Connection Failed");
}
#Override
public void onConnected(Bundle connectionHint) {
try{
Intent intent = new Intent(ctx, ActivitySensor.class);
Bundle bundle = intent.getExtras();
callbackIntent = PendingIntent.getService(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 5000;
if ( null!= bundle && bundle.containsKey("LOG_INTERVAL") ){
interval = bundle.getLong("LOG_INTERVAL");
}
actrecClient.requestActivityUpdates(interval, callbackIntent);
actrecClient.disconnect();
}catch(Exception ex){
Log.e("[Activity-Logging]","Error in requesting Activity update "+ex.getMessage());
ex.printStackTrace();
}
}
#Override
public void onDisconnected() {
callbackIntent.cancel();
actrecClient = null;
Log.e("[ActivityRecognitionScan]","---onDisconnected");
}
}
IntentService automatically stops itself on completion of onHandleIntent as per the source code (see ServiceHandler.handleMessage()) as per the description of an IntentService:
Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Use a Service if you want it to run continuously in the background.
You have 2 issues with your code that is causing the problem you are experiencing.
When activity is detected, the pending intent that is called calls (and creates, since it is an IntentService) ActivitySensor. The onCreate will connect another ActivityRecognitionClient, which is unnecessary. This causes another activity to be detected which causes your logging loop.
You should separate the creation of the ActivityRecognitionClient from the handling of the detected activity. You don't need to keep recreating it as subsequent detections will use the same PendingIntent. This will prevent the logging loop.

unable to start IntentService

I am getting the NPE while starting the service. I have just gone through the service tutorial on android developer site.
Logs are showing unable to resume activity...
#Override
protected void onResume() {
super.onResume();
CustomIntentService cis = new CustomIntentService();
Intent intent1 = new Intent(this, CustomIntentService.class);
intent1.putExtra("NUM", 1);
cis.startService(intent1);
}
My service is :
public class CustomIntentService extends IntentService {
private final static String TAG = "CustomIntentService";
public CustomIntentService() {
super("CustomIntentService");
Log.d(TAG,"out CustomIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent");
Log.d(TAG, "service num = " + intent.getIntExtra("NUM", 0));
if (Looper.getMainLooper() == Looper.myLooper()) {
Log.d(TAG, "In main ui thread");
} else {
Log.d(TAG, "In worker thread");
}
}
}
Change your code for onResume to the following:
#Override
protected void onResume() {
super.onResume();
//CustomIntentService cis = new CustomIntentService();
Intent intent1 = new Intent(this, CustomIntentService.class);
intent1.putExtra("NUM", 1);
startService(intent1);
}
This should fix the issue, remember intent knows which service to start and startService() is called on context. So here activity's instance will be the context.
Also,
Since Service is a component, so you should be declaring it in the AndroidManifestFile
<service
android:name=".CustomIntentService">
</service>
*Note: CustomIntentService should be under current directory, or you can supply absolute path also.
You can refer this

onServiceConnected sometimes not called after bindService on some devices

I've looked at a number of other threads with similar titles, and none seem to cover my problem. So, here goes.
I'm using the Google market expansion files (apkx) library and sample code, with a few modifications. This code relies on receiving callbacks from a service which handles background downloading, licence checks etc.
I have a bug where the service doesn't get correctly attached, which results in a softlock. To make this more unhelpful, this bug never happens on some devices, but occurs about two thirds of the time on other devices. I believe it to be independent of Android version, certainly I have two devices running 2.3.4, one of which (a Nexus S) doesn't have the problem, the other (an HTC Evo 3D) does.
To attempt to connect to the service, bindService is called and returns true. OnBind then gets called as expected and returns a sensible value but (when the bug occurs) onServiceConnected doesn't happen (I've waited 20 minutes just in case).
Has anyone else seen anything like this? If not, any guesses for what I might have done to cause such behaviour? If no-one has any thoughts, I'll post some code tomorrow.
EDIT: Here's the relevant code. If I've missed anything, please ask.
Whilst adding this code, I found a minor bug. Fixing it caused the frequency of the problem I'm trying to solve to change from 2 times in 3 to about 1 time in 6 on the phone I'm testing it on; no idea about effects on other phones. This continues to suggest to me a race condition or similar, but I've no idea what with.
OurDownloaderActivity.java (copied and changed from Google sample code)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
//Test the licence is up to date
//if (current stored licence has expired)
{
startLicenceCheck();
initializeDownloadUI();
return;
}
...
}
#Override
protected void onResume() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.connect(this);
}
super.onResume();
}
private void startLicenceCheck()
{
Intent launchIntent = OurDownloaderActivity.this
.getIntent();
Intent intentToLaunchThisActivityFromNotification = new Intent(OurDownloaderActivity
.this, OurDownloaderActivity.this.getClass());
intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction());
if (launchIntent.getCategories() != null) {
for (String category : launchIntent.getCategories()) {
intentToLaunchThisActivityFromNotification.addCategory(category);
}
}
// Build PendingIntent used to open this activity from Notification
PendingIntent pendingIntent = PendingIntent.getActivity(OurDownloaderActivity.this,
0, intentToLaunchThisActivityFromNotification,
PendingIntent.FLAG_UPDATE_CURRENT);
DownloaderService.startLicenceCheck(this, pendingIntent, OurDownloaderService.class);
}
initializeDownloadUI()
{
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub
(this, OurDownloaderService.class);
//do a load of UI setup
...
}
//This should be called by the Stub's onServiceConnected method
/**
* Critical implementation detail. In onServiceConnected we create the
* remote service and marshaler. This is how we pass the client information
* back to the service so the client can be properly notified of changes. We
* must do this every time we reconnect to the service.
*/
#Override
public void onServiceConnected(Messenger m) {
mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}
DownloaderService.java (in Google market expansion library but somewhat edited )
//this is the onBind call that happens fine; the value it returns is definitely not null
#Override
public IBinder onBind(Intent paramIntent) {
return this.mServiceMessenger.getBinder();
}
final private IStub mServiceStub = DownloaderServiceMarshaller.CreateStub(this);
final private Messenger mServiceMessenger = mServiceStub.getMessenger();
//MY CODE, derived from Google's code
//I have seen the bug occur with a service started by Google's code too,
//but this code happens more often so is more repeatably related to the problem
public static void startLicenceCheck(Context context, PendingIntent pendingIntent, Class<?> serviceClass)
{
String packageName = serviceClass.getPackage().getName();
String className = serviceClass.getName();
Intent fileIntent = new Intent();
fileIntent.setClassName(packageName, className);
fileIntent.putExtra(EXTRA_LICENCE_EXPIRED, true);
fileIntent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
context.startService(fileIntent);
}
#Override
protected void onHandleIntent(Intent intent) {
setServiceRunning(true);
try {
final PendingIntent pendingIntent = (PendingIntent) intent
.getParcelableExtra(EXTRA_PENDING_INTENT);
if (null != pendingIntent)
{
mNotification.setClientIntent(pendingIntent);
mPendingIntent = pendingIntent;
} else if (null != mPendingIntent) {
mNotification.setClientIntent(mPendingIntent);
} else {
Log.e(LOG_TAG, "Downloader started in bad state without notification intent.");
return;
}
if(intent.getBooleanExtra(EXTRA_LICENCE_EXPIRED, false))
{
//we are here due to startLicenceCheck
updateExpiredLVL(this);
return;
}
...
}
}
//MY CODE, based on Google's, again
public void updateExpiredLVL(final Context context) {
Context c = context.getApplicationContext();
Handler h = new Handler(c.getMainLooper());
h.post(new LVLExpiredUpdateRunnable(c));
}
private class LVLExpiredUpdateRunnable implements Runnable
{
LVLExpiredUpdateRunnable(Context context) {
mContext = context;
}
final Context mContext;
#Override
public void run() {
setServiceRunning(true);
mNotification.onDownloadStateChanged(IDownloaderClient.STATE_LVL_UPDATING);
String deviceId = getDeviceId(mContext);
final APKExpansionPolicy aep = new APKExpansionPolicy(mContext,
new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId));
// Construct the LicenseChecker with a Policy.
final LicenseChecker checker = new LicenseChecker(mContext, aep,
getPublicKey() // Your public licensing key.
);
checker.checkAccess(new LicenseCheckerCallback() {
...
});
}
}
DownloaderClientMarshaller.java (in Google market expansion library)
public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
return new Stub(itf, downloaderService);
}
and the Stub class from the same file:
private static class Stub implements IStub {
private IDownloaderClient mItf = null;
private Class<?> mDownloaderServiceClass;
private boolean mBound;
private Messenger mServiceMessenger;
private Context mContext;
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ONDOWNLOADPROGRESS:
Bundle bun = msg.getData();
if ( null != mContext ) {
bun.setClassLoader(mContext.getClassLoader());
DownloadProgressInfo dpi = (DownloadProgressInfo) msg.getData()
.getParcelable(PARAM_PROGRESS);
mItf.onDownloadProgress(dpi);
}
break;
case MSG_ONDOWNLOADSTATE_CHANGED:
mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE));
break;
case MSG_ONSERVICECONNECTED:
mItf.onServiceConnected(
(Messenger) msg.getData().getParcelable(PARAM_MESSENGER));
break;
}
}
});
public Stub(IDownloaderClient itf, Class<?> downloaderService) {
mItf = itf;
mDownloaderServiceClass = downloaderService;
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
//this is the critical call that never happens
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mServiceMessenger = new Messenger(service);
mItf.onServiceConnected(
mServiceMessenger);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mServiceMessenger = null;
mBound = false;
}
};
#Override
public void connect(Context c) {
mContext = c;
Intent bindIntent = new Intent(c, mDownloaderServiceClass);
bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
if ( !c.bindService(bindIntent, mConnection, 0) ) {
if ( Constants.LOGVV ) {
Log.d(Constants.TAG, "Service Unbound");
}
}
}
#Override
public void disconnect(Context c) {
if (mBound) {
c.unbindService(mConnection);
mBound = false;
}
mContext = null;
}
#Override
public Messenger getMessenger() {
return mMessenger;
}
}
DownloaderServiceMarshaller.java (in Google market expansion library, unchanged)
private static class Proxy implements IDownloaderService {
private Messenger mMsg;
private void send(int method, Bundle params) {
Message m = Message.obtain(null, method);
m.setData(params);
try {
mMsg.send(m);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Proxy(Messenger msg) {
mMsg = msg;
}
#Override
public void requestAbortDownload() {
send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
}
#Override
public void requestPauseDownload() {
send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
}
#Override
public void setDownloadFlags(int flags) {
Bundle params = new Bundle();
params.putInt(PARAMS_FLAGS, flags);
send(MSG_SET_DOWNLOAD_FLAGS, params);
}
#Override
public void requestContinueDownload() {
send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
}
#Override
public void requestDownloadStatus() {
send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
}
#Override
public void onClientUpdated(Messenger clientMessenger) {
Bundle bundle = new Bundle(1);
bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
send(MSG_REQUEST_CLIENT_UPDATE, bundle);
}
}
private static class Stub implements IStub {
private IDownloaderService mItf = null;
final Messenger mMessenger = new Messenger(new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REQUEST_ABORT_DOWNLOAD:
mItf.requestAbortDownload();
break;
case MSG_REQUEST_CONTINUE_DOWNLOAD:
mItf.requestContinueDownload();
break;
case MSG_REQUEST_PAUSE_DOWNLOAD:
mItf.requestPauseDownload();
break;
case MSG_SET_DOWNLOAD_FLAGS:
mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
break;
case MSG_REQUEST_DOWNLOAD_STATE:
mItf.requestDownloadStatus();
break;
case MSG_REQUEST_CLIENT_UPDATE:
mItf.onClientUpdated((Messenger) msg.getData().getParcelable(
PARAM_MESSENGER));
break;
}
}
});
public Stub(IDownloaderService itf) {
mItf = itf;
}
#Override
public Messenger getMessenger() {
return mMessenger;
}
#Override
public void connect(Context c) {
}
#Override
public void disconnect(Context c) {
}
}
/**
* Returns a proxy that will marshall calls to IDownloaderService methods
*
* #param ctx
* #return
*/
public static IDownloaderService CreateProxy(Messenger msg) {
return new Proxy(msg);
}
/**
* Returns a stub object that, when connected, will listen for marshalled
* IDownloaderService methods and translate them into calls to the supplied
* interface.
*
* #param itf An implementation of IDownloaderService that will be called
* when remote method calls are unmarshalled.
* #return
*/
public static IStub CreateStub(IDownloaderService itf) {
return new Stub(itf);
}

Categories

Resources