I get this exception from crash reports:
java.lang.RuntimeException: Unable to start service com.problemio.BillingService@4132b868 with Intent { act=com.android.vending.billing.PURCHASE_STATE_CHANGED cmp=com.problemio/.BillingService (has extras) }: java.lang.IllegalArgumentException: utils.Base64DecoderException: single trailing character at offset 19 at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2376) at android.app.ActivityThread.access$1900(ActivityThread.java:123) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4424) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.IllegalArgumentException: utils.Base64DecoderException: single trailing character at offset 19 at utils.Security.generatePublicKey(Security.java:199) at utils.Security.verifyPurchase(Security.java:118) at com.problemio.BillingService.purchaseStateChanged(BillingService.java:545) at com.problemio.BillingService.handleCommand(BillingService.java:421) at com.problemio.BillingService.onStart(BillingService.java:398) at android.app.Service.onStartCommand(Service.java:438) at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2359) ... 10 more Caused by: utils.Base64DecoderException: single trailing character at offset 19 at utils.Base64.decode(Base64.java:529) at utils.Base64.decode(Base64.java:444) at utils.Base64.decode(Base64.java:390) at utils.Security.generatePublicKey(Security.java:189) ... 16 more java.lang.IllegalArgumentException: utils.Base64DecoderException: single trailing character at offset 19 at utils.Security.generatePublicKey(Security.java:199) at utils.Security.verifyPurchase(Security.java:118) at com.problemio.BillingService.purchaseStateChanged(BillingService.java:545) at com.problemio.BillingService.handleCommand(BillingService.java:421) at com.problemio.BillingService.onStart(BillingService.java:398) at android.app.Service.onStartCommand(Service.java:438) at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2359) at android.app.ActivityThread.access$1900(ActivityThread.java:123) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4424) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) at dalvik.system.NativeStart.main(Native Method) Caused by: utils.Base64DecoderException: single trailing character at offset 19 at utils.Base64.decode(Base64.java:529) at utils.Base64.decode(Base64.java:444) at utils.Base64.decode(Base64.java:390) at utils.Security.generatePublicKey(Security.java:189) ... 16 more utils.Base64DecoderException: single trailing character at offset 19 at utils.Base64.decode(Base64.java:529) at utils.Base64.decode(Base64.java:444) at utils.Base64.decode(Base64.java:390) at utils.Security.generatePublicKey(Security.java:189) at utils.Security.verifyPurchase(Security.java:118) at com.problemio.BillingService.purchaseStateChanged(BillingService.java:545) at com.problemio.BillingService.handleCommand(BillingService.java:421) at com.problemio.BillingService.onStart(BillingService.java:398) at android.app.Service.onStartCommand(Service.java:438) at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2359) at android.app.ActivityThread.access$1900(ActivityThread.java:123) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4424) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) at dalvik.system.NativeStart.main(Native Method)
but I donβt quite understand what the problem is. Any suggestions?
He points to this method:
/** * Decodes Base64 content using the supplied decodabet and returns * the decoded byte array. * * @param source the Base64 encoded data * @param off the offset of where to begin decoding * @param len the length of characters to decode * @param decodabet the decodabet for decoding Base64 content * @return decoded data */ public static byte[] decode(byte[] source, int off, int len, byte[] decodabet) throws Base64DecoderException { int len34 = len * 3 / 4; byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output int outBuffPosn = 0; byte[] b4 = new byte[4]; int b4Posn = 0; int i = 0; byte sbiCrop = 0; byte sbiDecode = 0; for (i = 0; i < len; i++) { sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits sbiDecode = decodabet[sbiCrop]; if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better if (sbiDecode >= EQUALS_SIGN_ENC) { // An equals sign (for padding) must not occur at position 0 or 1 // and must be the last byte[s] in the encoded value if (sbiCrop == EQUALS_SIGN) { int bytesLeft = len - i; byte lastByte = (byte) (source[len - 1 + off] & 0x7f); if (b4Posn == 0 || b4Posn == 1) { throw new Base64DecoderException( "invalid padding byte '=' at byte offset " + i); } else if ((b4Posn == 3 && bytesLeft > 2) || (b4Posn == 4 && bytesLeft > 1)) { throw new Base64DecoderException( "padding byte '=' falsely signals end of encoded value " + "at offset " + i); } else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) { throw new Base64DecoderException( "encoded value has invalid trailing byte"); } break; } b4[b4Posn++] = sbiCrop; if (b4Posn == 4) { outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet); b4Posn = 0; } } } else { throw new Base64DecoderException("Bad Base64 input character at " + i + ": " + source[i + off] + "(decimal)"); } } // Because web safe encoding allows non padding base64 encodes, we // need to pad the rest of the b4 buffer with equal signs when // b4Posn != 0. There can be at most 2 equal signs at the end of // four characters, so the b4 buffer must have two or three // characters. This also catches the case where the input is // padded with EQUALS_SIGN if (b4Posn != 0) { if (b4Posn == 1) { throw new Base64DecoderException("single trailing character at offset " + (len - 1)); } b4[b4Posn++] = EQUALS_SIGN; outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet); } byte[] out = new byte[outBuffPosn]; System.arraycopy(outBuff, 0, out, 0, outBuffPosn); return out; }
The error seems to point to these lines in my BillingService.java
at com.problemio.BillingService.purchaseStateChanged(BillingService.java:585) at com.problemio.BillingService.handleCommand(BillingService.java:461) at com.problemio.BillingService.onStart(BillingService.java:438)
The line at 585 is the line
purchases = Security.verifyPurchase(signedData, signature);
in this method:
private void purchaseStateChanged(int startId, String signedData, String signature) { ArrayList<Security.VerifiedPurchase> purchases; purchases = Security.verifyPurchase(signedData, signature); if (purchases == null) { return; }
and line 461 is the line:
purchaseStateChanged(startId, signedData, signature);
in this method:
public void handleCommand(Intent intent, int startId) { String action = intent.getAction(); if (Consts.DEBUG) { Log.i(TAG, "handleCommand() action: " + action); } if (Consts.ACTION_CONFIRM_NOTIFICATION.equals(action)) { String[] notifyIds = intent.getStringArrayExtra(Consts.NOTIFICATION_ID); confirmNotifications(startId, notifyIds); } else if (Consts.ACTION_GET_PURCHASE_INFORMATION.equals(action)) { String notifyId = intent.getStringExtra(Consts.NOTIFICATION_ID); getPurchaseInformation(startId, new String[] { notifyId }); } else if (Consts.ACTION_PURCHASE_STATE_CHANGED.equals(action)) { String signedData = intent.getStringExtra(Consts.INAPP_SIGNED_DATA); String signature = intent.getStringExtra(Consts.INAPP_SIGNATURE); purchaseStateChanged(startId, signedData, signature); } else if (Consts.ACTION_RESPONSE_CODE.equals(action)) { long requestId = intent.getLongExtra(Consts.INAPP_REQUEST_ID, -1); int responseCodeIndex = intent.getIntExtra(Consts.INAPP_RESPONSE_CODE, ResponseCode.RESULT_ERROR.ordinal()); ResponseCode responseCode = ResponseCode.valueOf(responseCodeIndex); checkResponseCode(requestId, responseCode); } }
and line 438 is the line:
handleCommand(intent, startId);
in this method:
@Override public void onStart(Intent intent, int startId) { handleCommand(intent, startId); }
Thanks!