diff --git a/onebusaway-android/src/androidTest/java/org/onebusaway/android/directions/realtime/RealtimeServiceTest.java b/onebusaway-android/src/androidTest/java/org/onebusaway/android/directions/realtime/RealtimeServiceTest.java index d83215f58..81e249a64 100644 --- a/onebusaway-android/src/androidTest/java/org/onebusaway/android/directions/realtime/RealtimeServiceTest.java +++ b/onebusaway-android/src/androidTest/java/org/onebusaway/android/directions/realtime/RealtimeServiceTest.java @@ -151,21 +151,11 @@ public void testOnHandleIntentWithEmptyBundleDoesNotCrash() { } @Test - public void testGetSimplifiedBundleWithMissingItineraryDoesNotCrash() throws Exception { - // Bundle with no itineraries and no selected index - Bundle badBundle = new Bundle(); - - Bundle result = mService.getSimplifiedBundle(badBundle); - // After the fix, getSimplifiedBundle() should handle the null itinerary gracefully - // and return null instead of crashing. - assertNull("getSimplifiedBundle should return null for missing itinerary", result); - } - - @Test - public void testGetSimplifiedBundleWithMissingNotificationTargetDoesNotCrash() throws Exception { + public void testOnHandleIntentWithRealtimeLegsButNoNotificationTargetDoesNotCrash() { Bundle bundle = new Bundle(); - // Minimal from/to and date so TripRequestBuilder.copyIntoBundleSimple() does not throw. + // Minimal from/to and date so TripRequestBuilder.copyIntoBundleSimple() does not throw + // when getSimplifiedBundle is called during startRealtimeUpdates. CustomAddress from = new CustomAddress(); from.setLatitude(0); from.setLongitude(0); @@ -174,25 +164,31 @@ public void testGetSimplifiedBundleWithMissingNotificationTargetDoesNotCrash() t to.setLongitude(0); new TripRequestBuilder(bundle).setFrom(from).setTo(to).setDateTime(new Date()); - // Build a valid itinerary with one transit leg so ItineraryDescription succeeds - // and we actually reach the NOTIFICATION_TARGET check (not the catch block). + // Build a valid itinerary with realtime transit leg so startRealtimeUpdates proceeds + // and getAlarmIntent/getSimplifiedBundle are invoked. No NOTIFICATION_TARGET exercises + // the alarmIntent == null guard in startRealtimeUpdates (lines 125-129). + ArrayList itineraries = new ArrayList<>(); Itinerary it = new Itinerary(); Leg leg = new Leg(); - leg.mode = "BUS"; leg.realTime = true; + leg.mode = "BUS"; leg.tripId = "testTrip"; leg.endTime = String.valueOf(System.currentTimeMillis() + 3600000); it.legs = new ArrayList<>(); it.legs.add(leg); - - ArrayList itineraries = new ArrayList<>(); itineraries.add(it); bundle.putSerializable(OTPConstants.ITINERARIES, itineraries); bundle.putInt(OTPConstants.SELECTED_ITINERARY, 0); - // Intentionally DO NOT put OTPConstants.NOTIFICATION_TARGET into the bundle - Bundle result = mService.getSimplifiedBundle(bundle); - // After the fix, getSimplifiedBundle() should handle missing NOTIFICATION_TARGET - // gracefully and return null instead of crashing. - assertNull("getSimplifiedBundle should return null when NOTIFICATION_TARGET is missing", result); + // No NOTIFICATION_TARGET — exercises the null alarmIntent guard in startRealtimeUpdates + + Intent intent = new Intent(OTPConstants.INTENT_START_CHECKS); + intent.putExtras(bundle); + + try { + mService.onHandleIntent(intent); + } catch (NullPointerException e) { + fail("Should not crash with realtime legs but missing NOTIFICATION_TARGET: " + + e.getMessage()); + } } } diff --git a/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeService.java b/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeService.java index 73908fe86..6f64b9090 100644 --- a/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeService.java +++ b/onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/RealtimeService.java @@ -111,9 +111,11 @@ private void startRealtimeUpdates(Bundle params, Itinerary itinerary) { boolean realtimeLegsOnItineraries = false; - for (Leg leg : itinerary.legs) { - if (leg.realTime) { - realtimeLegsOnItineraries = true; + if (itinerary.legs != null) { + for (Leg leg : itinerary.legs) { + if (leg.realTime) { + realtimeLegsOnItineraries = true; + } } } @@ -304,7 +306,10 @@ private void checkDisableDueToTimeout(ItineraryDescription itineraryDescription) public void disableListenForTripUpdates() { Log.d(TAG, "Disable trip updates."); - getAlarmManager().cancel(getAlarmIntent(null)); + PendingIntent alarmIntent = getAlarmIntent(null); + if (alarmIntent != null) { + getAlarmManager().cancel(alarmIntent); + } } private AlarmManager getAlarmManager() { @@ -369,13 +374,11 @@ Bundle getSimplifiedBundle(Bundle params) { return null; } - ItineraryDescription desc; - try { - desc = new ItineraryDescription(itinerary); - } catch (NullPointerException | IndexOutOfBoundsException e) { - Log.e(TAG, "getSimplifiedBundle: error creating ItineraryDescription", e); + if (itinerary.legs == null || itinerary.legs.isEmpty()) { + Log.w(TAG, "getSimplifiedBundle: itinerary has no legs"); return null; } + ItineraryDescription desc = new ItineraryDescription(itinerary); Bundle extras = new Bundle(); try {