39
39
import io .sentry .util .StringUtils ;
40
40
import java .io .Closeable ;
41
41
import java .io .IOException ;
42
- import java .util .ArrayList ;
42
+ import java .util .Arrays ;
43
43
import java .util .HashMap ;
44
44
import java .util .List ;
45
45
import java .util .Map ;
@@ -55,19 +55,25 @@ public final class SystemEventsBreadcrumbsIntegration implements Integration, Cl
55
55
56
56
private @ Nullable SentryAndroidOptions options ;
57
57
58
- private final @ NotNull List < String > actions ;
58
+ private final @ NotNull String [] actions ;
59
59
private boolean isClosed = false ;
60
60
private final @ NotNull AutoClosableReentrantLock startLock = new AutoClosableReentrantLock ();
61
61
62
62
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 ;
64
70
}
65
71
66
72
public SystemEventsBreadcrumbsIntegration (
67
73
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 );
71
77
}
72
78
73
79
@ Override
@@ -129,28 +135,32 @@ private void startSystemEventsReceiver(
129
135
}
130
136
}
131
137
132
- @ SuppressWarnings ("deprecation" )
133
138
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" ;
154
164
return actions ;
155
165
}
156
166
@@ -206,10 +216,43 @@ public void onReceive(final Context context, final @NotNull Intent intent) {
206
216
scopes .addBreadcrumb (breadcrumb , hint );
207
217
});
208
218
} 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 ;
212
234
}
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 ;
213
256
}
214
257
215
258
private @ NotNull Breadcrumb createBreadcrumb (
@@ -220,7 +263,7 @@ public void onReceive(final Context context, final @NotNull Intent intent) {
220
263
final Breadcrumb breadcrumb = new Breadcrumb (timeMs );
221
264
breadcrumb .setType ("system" );
222
265
breadcrumb .setCategory ("device.event" );
223
- final String shortAction = StringUtils . getStringAfterDot (action );
266
+ final String shortAction = getStringAfterDotFast (action );
224
267
if (shortAction != null ) {
225
268
breadcrumb .setData ("action" , shortAction );
226
269
}
0 commit comments