3939import io .sentry .util .StringUtils ;
4040import java .io .Closeable ;
4141import java .io .IOException ;
42- import java .util .ArrayList ;
42+ import java .util .Arrays ;
4343import java .util .HashMap ;
4444import java .util .List ;
4545import java .util .Map ;
@@ -55,19 +55,25 @@ public final class SystemEventsBreadcrumbsIntegration implements Integration, Cl
5555
5656 private @ Nullable SentryAndroidOptions options ;
5757
58- private final @ NotNull List < String > actions ;
58+ private final @ NotNull String [] actions ;
5959 private boolean isClosed = false ;
6060 private final @ NotNull AutoClosableReentrantLock startLock = new AutoClosableReentrantLock ();
6161
6262 public SystemEventsBreadcrumbsIntegration (final @ NotNull Context context ) {
63- this (context , getDefaultActions ());
63+ this (context , getDefaultActionsInternal ());
64+ }
65+
66+ private SystemEventsBreadcrumbsIntegration (
67+ final @ NotNull Context context , final @ NotNull String [] actions ) {
68+ this .context = ContextUtils .getApplicationContext (context );
69+ this .actions = actions ;
6470 }
6571
6672 public SystemEventsBreadcrumbsIntegration (
6773 final @ NotNull Context context , final @ NotNull List <String > actions ) {
68- this .context =
69- Objects . requireNonNull ( ContextUtils . getApplicationContext ( context ), "Context is required" ) ;
70- this . actions = Objects . requireNonNull ( actions , "Actions list is required" );
74+ this .context = ContextUtils . getApplicationContext ( context );
75+ this . actions = new String [ actions . size ()] ;
76+ actions . toArray ( this . actions );
7177 }
7278
7379 @ Override
@@ -129,28 +135,32 @@ private void startSystemEventsReceiver(
129135 }
130136 }
131137
132- @ SuppressWarnings ("deprecation" )
133138 public static @ NotNull List <String > getDefaultActions () {
134- final List <String > actions = new ArrayList <>();
135- actions .add (ACTION_SHUTDOWN );
136- actions .add (ACTION_AIRPLANE_MODE_CHANGED );
137- actions .add (ACTION_BATTERY_CHANGED );
138- actions .add (ACTION_CAMERA_BUTTON );
139- actions .add (ACTION_CONFIGURATION_CHANGED );
140- actions .add (ACTION_DATE_CHANGED );
141- actions .add (ACTION_DEVICE_STORAGE_LOW );
142- actions .add (ACTION_DEVICE_STORAGE_OK );
143- actions .add (ACTION_DOCK_EVENT );
144- actions .add (ACTION_DREAMING_STARTED );
145- actions .add (ACTION_DREAMING_STOPPED );
146- actions .add (ACTION_INPUT_METHOD_CHANGED );
147- actions .add (ACTION_LOCALE_CHANGED );
148- actions .add (ACTION_SCREEN_OFF );
149- actions .add (ACTION_SCREEN_ON );
150- actions .add (ACTION_TIMEZONE_CHANGED );
151- actions .add (ACTION_TIME_CHANGED );
152- actions .add ("android.os.action.DEVICE_IDLE_MODE_CHANGED" );
153- actions .add ("android.os.action.POWER_SAVE_MODE_CHANGED" );
139+ return Arrays .asList (getDefaultActionsInternal ());
140+ }
141+
142+ @ SuppressWarnings ("deprecation" )
143+ private static @ NotNull String [] getDefaultActionsInternal () {
144+ final String [] actions = new String [19 ];
145+ actions [0 ] = ACTION_SHUTDOWN ;
146+ actions [1 ] = ACTION_AIRPLANE_MODE_CHANGED ;
147+ actions [2 ] = ACTION_BATTERY_CHANGED ;
148+ actions [3 ] = ACTION_CAMERA_BUTTON ;
149+ actions [4 ] = ACTION_CONFIGURATION_CHANGED ;
150+ actions [5 ] = ACTION_DATE_CHANGED ;
151+ actions [6 ] = ACTION_DEVICE_STORAGE_LOW ;
152+ actions [7 ] = ACTION_DEVICE_STORAGE_OK ;
153+ actions [8 ] = ACTION_DOCK_EVENT ;
154+ actions [9 ] = ACTION_DREAMING_STARTED ;
155+ actions [10 ] = ACTION_DREAMING_STOPPED ;
156+ actions [11 ] = ACTION_INPUT_METHOD_CHANGED ;
157+ actions [12 ] = ACTION_LOCALE_CHANGED ;
158+ actions [13 ] = ACTION_SCREEN_OFF ;
159+ actions [14 ] = ACTION_SCREEN_ON ;
160+ actions [15 ] = ACTION_TIMEZONE_CHANGED ;
161+ actions [16 ] = ACTION_TIME_CHANGED ;
162+ actions [17 ] = "android.os.action.DEVICE_IDLE_MODE_CHANGED" ;
163+ actions [18 ] = "android.os.action.POWER_SAVE_MODE_CHANGED" ;
154164 return actions ;
155165 }
156166
@@ -206,10 +216,43 @@ public void onReceive(final Context context, final @NotNull Intent intent) {
206216 scopes .addBreadcrumb (breadcrumb , hint );
207217 });
208218 } catch (Throwable t ) {
209- options
210- .getLogger ()
211- .log (SentryLevel .ERROR , t , "Failed to submit system event breadcrumb action." );
219+ // ignored
220+ }
221+ }
222+
223+ // in theory this should be ThreadLocal, but we won't have more than 1 thread accessing it,
224+ // so we save some memory here and CPU cycles. 64 is because all intent actions we subscribe for
225+ // are less than 64 chars. We also don't care about encoding as those are always UTF.
226+ // TODO: _MULTI_THREADED_EXECUTOR_
227+ private final char [] buf = new char [64 ];
228+
229+ @ TestOnly
230+ @ Nullable
231+ String getStringAfterDotFast (final @ Nullable String str ) {
232+ if (str == null ) {
233+ return null ;
212234 }
235+
236+ final int len = str .length ();
237+ int bufIndex = buf .length ;
238+
239+ // the idea here is to iterate from the end of the string and copy the characters to a
240+ // pre-allocated buffer in reverse order. When we find a dot, we create a new string
241+ // from the buffer. This way we use a fixed size buffer and do a bare minimum of iterations.
242+ for (int i = len - 1 ; i >= 0 ; i --) {
243+ final char c = str .charAt (i );
244+ if (c == '.' ) {
245+ return new String (buf , bufIndex , buf .length - bufIndex );
246+ }
247+ if (bufIndex == 0 ) {
248+ // Overflow — fallback to safe version
249+ return StringUtils .getStringAfterDot (str );
250+ }
251+ buf [--bufIndex ] = c ;
252+ }
253+
254+ // No dot found — return original
255+ return str ;
213256 }
214257
215258 private @ NotNull Breadcrumb createBreadcrumb (
@@ -220,7 +263,7 @@ public void onReceive(final Context context, final @NotNull Intent intent) {
220263 final Breadcrumb breadcrumb = new Breadcrumb (timeMs );
221264 breadcrumb .setType ("system" );
222265 breadcrumb .setCategory ("device.event" );
223- final String shortAction = StringUtils . getStringAfterDot (action );
266+ final String shortAction = getStringAfterDotFast (action );
224267 if (shortAction != null ) {
225268 breadcrumb .setData ("action" , shortAction );
226269 }
0 commit comments