3737import io .sentry .util .StringUtils ;
3838import java .io .Closeable ;
3939import java .io .IOException ;
40- import java .util .ArrayList ;
40+ import java .util .Arrays ;
4141import java .util .HashMap ;
4242import java .util .List ;
4343import java .util .Map ;
@@ -53,19 +53,25 @@ public final class SystemEventsBreadcrumbsIntegration implements Integration, Cl
5353
5454 private @ Nullable SentryAndroidOptions options ;
5555
56- private final @ NotNull List < String > actions ;
56+ private final @ NotNull String [] actions ;
5757 private boolean isClosed = false ;
5858 private final @ NotNull Object startLock = new Object ();
5959
6060 public SystemEventsBreadcrumbsIntegration (final @ NotNull Context context ) {
61- this (context , getDefaultActions ());
61+ this (context , getDefaultActionsInternal ());
62+ }
63+
64+ private SystemEventsBreadcrumbsIntegration (
65+ final @ NotNull Context context , final @ NotNull String [] actions ) {
66+ this .context = ContextUtils .getApplicationContext (context );
67+ this .actions = actions ;
6268 }
6369
6470 public SystemEventsBreadcrumbsIntegration (
6571 final @ NotNull Context context , final @ NotNull List <String > actions ) {
66- this .context =
67- Objects . requireNonNull ( ContextUtils . getApplicationContext ( context ), "Context is required" ) ;
68- this . actions = Objects . requireNonNull ( actions , "Actions list is required" );
72+ this .context = ContextUtils . getApplicationContext ( context );
73+ this . actions = new String [ actions . size ()] ;
74+ actions . toArray ( this . actions );
6975 }
7076
7177 @ Override
@@ -127,28 +133,32 @@ private void startSystemEventsReceiver(
127133 }
128134 }
129135
130- @ SuppressWarnings ("deprecation" )
131136 public static @ NotNull List <String > getDefaultActions () {
132- final List <String > actions = new ArrayList <>();
133- actions .add (ACTION_SHUTDOWN );
134- actions .add (ACTION_AIRPLANE_MODE_CHANGED );
135- actions .add (ACTION_BATTERY_CHANGED );
136- actions .add (ACTION_CAMERA_BUTTON );
137- actions .add (ACTION_CONFIGURATION_CHANGED );
138- actions .add (ACTION_DATE_CHANGED );
139- actions .add (ACTION_DEVICE_STORAGE_LOW );
140- actions .add (ACTION_DEVICE_STORAGE_OK );
141- actions .add (ACTION_DOCK_EVENT );
142- actions .add (ACTION_DREAMING_STARTED );
143- actions .add (ACTION_DREAMING_STOPPED );
144- actions .add (ACTION_INPUT_METHOD_CHANGED );
145- actions .add (ACTION_LOCALE_CHANGED );
146- actions .add (ACTION_SCREEN_OFF );
147- actions .add (ACTION_SCREEN_ON );
148- actions .add (ACTION_TIMEZONE_CHANGED );
149- actions .add (ACTION_TIME_CHANGED );
150- actions .add ("android.os.action.DEVICE_IDLE_MODE_CHANGED" );
151- actions .add ("android.os.action.POWER_SAVE_MODE_CHANGED" );
137+ return Arrays .asList (getDefaultActionsInternal ());
138+ }
139+
140+ @ SuppressWarnings ("deprecation" )
141+ private static @ NotNull String [] getDefaultActionsInternal () {
142+ final String [] actions = new String [19 ];
143+ actions [0 ] = ACTION_SHUTDOWN ;
144+ actions [1 ] = ACTION_AIRPLANE_MODE_CHANGED ;
145+ actions [2 ] = ACTION_BATTERY_CHANGED ;
146+ actions [3 ] = ACTION_CAMERA_BUTTON ;
147+ actions [4 ] = ACTION_CONFIGURATION_CHANGED ;
148+ actions [5 ] = ACTION_DATE_CHANGED ;
149+ actions [6 ] = ACTION_DEVICE_STORAGE_LOW ;
150+ actions [7 ] = ACTION_DEVICE_STORAGE_OK ;
151+ actions [8 ] = ACTION_DOCK_EVENT ;
152+ actions [9 ] = ACTION_DREAMING_STARTED ;
153+ actions [10 ] = ACTION_DREAMING_STOPPED ;
154+ actions [11 ] = ACTION_INPUT_METHOD_CHANGED ;
155+ actions [12 ] = ACTION_LOCALE_CHANGED ;
156+ actions [13 ] = ACTION_SCREEN_OFF ;
157+ actions [14 ] = ACTION_SCREEN_ON ;
158+ actions [15 ] = ACTION_TIMEZONE_CHANGED ;
159+ actions [16 ] = ACTION_TIME_CHANGED ;
160+ actions [17 ] = "android.os.action.DEVICE_IDLE_MODE_CHANGED" ;
161+ actions [18 ] = "android.os.action.POWER_SAVE_MODE_CHANGED" ;
152162 return actions ;
153163 }
154164
@@ -204,10 +214,43 @@ public void onReceive(final Context context, final @NotNull Intent intent) {
204214 hub .addBreadcrumb (breadcrumb , hint );
205215 });
206216 } catch (Throwable t ) {
207- options
208- .getLogger ()
209- .log (SentryLevel .ERROR , t , "Failed to submit system event breadcrumb action." );
217+ // ignored
218+ }
219+ }
220+
221+ // in theory this should be ThreadLocal, but we won't have more than 1 thread accessing it,
222+ // so we save some memory here and CPU cycles. 64 is because all intent actions we subscribe for
223+ // are less than 64 chars. We also don't care about encoding as those are always UTF.
224+ // TODO: _MULTI_THREADED_EXECUTOR_
225+ private final char [] buf = new char [64 ];
226+
227+ @ TestOnly
228+ @ Nullable
229+ String getStringAfterDotFast (final @ Nullable String str ) {
230+ if (str == null ) {
231+ return null ;
210232 }
233+
234+ final int len = str .length ();
235+ int bufIndex = buf .length ;
236+
237+ // the idea here is to iterate from the end of the string and copy the characters to a
238+ // pre-allocated buffer in reverse order. When we find a dot, we create a new string
239+ // from the buffer. This way we use a fixed size buffer and do a bare minimum of iterations.
240+ for (int i = len - 1 ; i >= 0 ; i --) {
241+ final char c = str .charAt (i );
242+ if (c == '.' ) {
243+ return new String (buf , bufIndex , buf .length - bufIndex );
244+ }
245+ if (bufIndex == 0 ) {
246+ // Overflow — fallback to safe version
247+ return StringUtils .getStringAfterDot (str );
248+ }
249+ buf [--bufIndex ] = c ;
250+ }
251+
252+ // No dot found — return original
253+ return str ;
211254 }
212255
213256 private @ NotNull Breadcrumb createBreadcrumb (
@@ -218,7 +261,7 @@ public void onReceive(final Context context, final @NotNull Intent intent) {
218261 final Breadcrumb breadcrumb = new Breadcrumb (timeMs );
219262 breadcrumb .setType ("system" );
220263 breadcrumb .setCategory ("device.event" );
221- final String shortAction = StringUtils . getStringAfterDot (action );
264+ final String shortAction = getStringAfterDotFast (action );
222265 if (shortAction != null ) {
223266 breadcrumb .setData ("action" , shortAction );
224267 }
0 commit comments