Skip to content

Commit a59c529

Browse files
authored
Added versionCode, applicationID, and customUUID from AndroidManifest.XML (if it exists) to crash and ANR spans (#993) (#1002)
1 parent 8568998 commit a59c529

File tree

5 files changed

+318
-22
lines changed

5 files changed

+318
-22
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.rum;
18+
19+
import android.app.Application;
20+
import android.content.pm.ApplicationInfo;
21+
import android.content.pm.PackageInfo;
22+
import android.content.pm.PackageManager;
23+
import android.os.Bundle;
24+
import android.util.Log;
25+
import androidx.annotation.NonNull;
26+
import androidx.annotation.Nullable;
27+
28+
public class ErrorIdentifierExtractor {
29+
30+
private static final String SPLUNK_UUID_MANIFEST_KEY = "SPLUNK_O11Y_CUSTOM_UUID";
31+
private final Application application;
32+
private final PackageManager packageManager;
33+
@Nullable private final ApplicationInfo applicationInfo;
34+
35+
public ErrorIdentifierExtractor(@NonNull Application application) {
36+
this.application = application;
37+
this.packageManager = application.getPackageManager();
38+
ApplicationInfo appInfo;
39+
try {
40+
appInfo =
41+
packageManager.getApplicationInfo(
42+
application.getPackageName(), PackageManager.GET_META_DATA);
43+
} catch (Exception e) {
44+
Log.e(
45+
SplunkRum.LOG_TAG,
46+
"Failed to initialize ErrorIdentifierExtractor: " + e.getMessage());
47+
appInfo = null;
48+
}
49+
this.applicationInfo = appInfo;
50+
}
51+
52+
public ErrorIdentifierInfo extractInfo() {
53+
String applicationId = null;
54+
String versionCode = retrieveVersionCode();
55+
String customUUID = retrieveCustomUUID();
56+
57+
if (applicationInfo != null) {
58+
applicationId = applicationInfo.packageName;
59+
} else {
60+
Log.e(SplunkRum.LOG_TAG, "ApplicationInfo is null, cannot extract applicationId");
61+
}
62+
63+
return new ErrorIdentifierInfo(applicationId, versionCode, customUUID);
64+
}
65+
66+
@Nullable
67+
private String retrieveVersionCode() {
68+
try {
69+
PackageInfo packageInfo =
70+
packageManager.getPackageInfo(application.getPackageName(), 0);
71+
return String.valueOf(packageInfo.versionCode);
72+
} catch (Exception e) {
73+
Log.e(SplunkRum.LOG_TAG, "Failed to get application version code", e);
74+
return null;
75+
}
76+
}
77+
78+
@Nullable
79+
private String retrieveCustomUUID() {
80+
if (applicationInfo == null) {
81+
Log.e(SplunkRum.LOG_TAG, "ApplicationInfo is null; cannot retrieve Custom UUID.");
82+
return null;
83+
}
84+
Bundle bundle = applicationInfo.metaData;
85+
if (bundle != null) {
86+
return bundle.getString(SPLUNK_UUID_MANIFEST_KEY);
87+
} else {
88+
Log.e(SplunkRum.LOG_TAG, "Application MetaData bundle is null");
89+
return null;
90+
}
91+
}
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.rum;
18+
19+
import androidx.annotation.Nullable;
20+
21+
public class ErrorIdentifierInfo {
22+
@Nullable private final String applicationId;
23+
@Nullable private final String versionCode;
24+
@Nullable private final String customUUID;
25+
26+
public ErrorIdentifierInfo(
27+
@Nullable String applicationId,
28+
@Nullable String versionCode,
29+
@Nullable String customUUID) {
30+
this.applicationId = applicationId;
31+
this.versionCode = versionCode;
32+
this.customUUID = customUUID;
33+
}
34+
35+
@Nullable
36+
public String getApplicationId() {
37+
return applicationId;
38+
}
39+
40+
@Nullable
41+
public String getVersionCode() {
42+
return versionCode;
43+
}
44+
45+
@Nullable
46+
public String getCustomUUID() {
47+
return customUUID;
48+
}
49+
}

splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java

+52-22
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616

1717
package com.splunk.rum;
1818

19+
import static com.splunk.rum.SplunkRum.APPLICATION_ID_KEY;
1920
import static com.splunk.rum.SplunkRum.APP_NAME_KEY;
21+
import static com.splunk.rum.SplunkRum.APP_VERSION_CODE_KEY;
2022
import static com.splunk.rum.SplunkRum.COMPONENT_APPSTART;
2123
import static com.splunk.rum.SplunkRum.COMPONENT_ERROR;
2224
import static com.splunk.rum.SplunkRum.COMPONENT_KEY;
2325
import static com.splunk.rum.SplunkRum.COMPONENT_UI;
2426
import static com.splunk.rum.SplunkRum.RUM_TRACER_NAME;
27+
import static com.splunk.rum.SplunkRum.SPLUNK_OLLY_UUID_KEY;
2528
import static io.opentelemetry.android.RumConstants.APP_START_SPAN_NAME;
2629
import static io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor.constant;
2730
import static io.opentelemetry.semconv.ResourceAttributes.DEPLOYMENT_ENVIRONMENT;
@@ -40,7 +43,9 @@
4043
import io.opentelemetry.android.config.OtelRumConfig;
4144
import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker;
4245
import io.opentelemetry.android.instrumentation.anr.AnrDetector;
46+
import io.opentelemetry.android.instrumentation.anr.AnrDetectorBuilder;
4347
import io.opentelemetry.android.instrumentation.crash.CrashReporter;
48+
import io.opentelemetry.android.instrumentation.crash.CrashReporterBuilder;
4449
import io.opentelemetry.android.instrumentation.lifecycle.AndroidLifecycleInstrumentation;
4550
import io.opentelemetry.android.instrumentation.network.CurrentNetworkProvider;
4651
import io.opentelemetry.android.instrumentation.slowrendering.SlowRenderingDetector;
@@ -286,45 +291,70 @@ private Resource createSplunkResource() {
286291
private void installAnrDetector(OpenTelemetryRumBuilder otelRumBuilder, Looper mainLooper) {
287292
otelRumBuilder.addInstrumentation(
288293
instrumentedApplication -> {
289-
AnrDetector.builder()
290-
.addAttributesExtractor(constant(COMPONENT_KEY, COMPONENT_ERROR))
291-
.setMainLooper(mainLooper)
292-
.build()
293-
.installOn(instrumentedApplication);
294+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(application);
295+
ErrorIdentifierInfo errorIdentifierInfo = extractor.extractInfo();
296+
String applicationId = errorIdentifierInfo.getApplicationId();
297+
String versionCode = errorIdentifierInfo.getVersionCode();
298+
String uuid = errorIdentifierInfo.getCustomUUID();
294299

295-
initializationEvents.emit("anrMonitorInitialized");
296-
});
297-
}
300+
AnrDetectorBuilder builder = AnrDetector.builder();
301+
builder.addAttributesExtractor(constant(COMPONENT_KEY, COMPONENT_ERROR));
298302

299-
private void installSlowRenderingDetector(OpenTelemetryRumBuilder otelRumBuilder) {
300-
otelRumBuilder.addInstrumentation(
301-
instrumentedApplication -> {
302-
SlowRenderingDetector.builder()
303-
.setSlowRenderingDetectionPollInterval(
304-
builder.slowRenderingDetectionPollInterval)
305-
.build()
306-
.installOn(instrumentedApplication);
307-
initializationEvents.emit("slowRenderingDetectorInitialized");
303+
if (applicationId != null)
304+
builder.addAttributesExtractor(constant(APPLICATION_ID_KEY, applicationId));
305+
if (versionCode != null)
306+
builder.addAttributesExtractor(constant(APP_VERSION_CODE_KEY, versionCode));
307+
if (uuid != null)
308+
builder.addAttributesExtractor(constant(SPLUNK_OLLY_UUID_KEY, uuid));
309+
310+
builder.setMainLooper(mainLooper).build().installOn(instrumentedApplication);
311+
312+
initializationEvents.emit("anrMonitorInitialized");
308313
});
309314
}
310315

311316
private void installCrashReporter(OpenTelemetryRumBuilder otelRumBuilder) {
312317
otelRumBuilder.addInstrumentation(
313318
instrumentedApplication -> {
314-
CrashReporter.builder()
315-
.addAttributesExtractor(
319+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(application);
320+
ErrorIdentifierInfo errorIdentifierInfo = extractor.extractInfo();
321+
String applicationId = errorIdentifierInfo.getApplicationId();
322+
String versionCode = errorIdentifierInfo.getVersionCode();
323+
String uuid = errorIdentifierInfo.getCustomUUID();
324+
325+
CrashReporterBuilder builder = CrashReporter.builder();
326+
builder.addAttributesExtractor(
316327
RuntimeDetailsExtractor.create(
317328
instrumentedApplication
318329
.getApplication()
319330
.getApplicationContext()))
320-
.addAttributesExtractor(new CrashComponentExtractor())
321-
.build()
322-
.installOn(instrumentedApplication);
331+
.addAttributesExtractor(new CrashComponentExtractor());
332+
333+
if (applicationId != null)
334+
builder.addAttributesExtractor(constant(APPLICATION_ID_KEY, applicationId));
335+
if (versionCode != null)
336+
builder.addAttributesExtractor(constant(APP_VERSION_CODE_KEY, versionCode));
337+
if (uuid != null)
338+
builder.addAttributesExtractor(constant(SPLUNK_OLLY_UUID_KEY, uuid));
339+
340+
builder.build().installOn(instrumentedApplication);
323341

324342
initializationEvents.emit("crashReportingInitialized");
325343
});
326344
}
327345

346+
private void installSlowRenderingDetector(OpenTelemetryRumBuilder otelRumBuilder) {
347+
otelRumBuilder.addInstrumentation(
348+
instrumentedApplication -> {
349+
SlowRenderingDetector.builder()
350+
.setSlowRenderingDetectionPollInterval(
351+
builder.slowRenderingDetectionPollInterval)
352+
.build()
353+
.installOn(instrumentedApplication);
354+
initializationEvents.emit("slowRenderingDetectorInitialized");
355+
});
356+
}
357+
328358
// visible for testing
329359
SpanExporter buildFilteringExporter(
330360
CurrentNetworkProvider currentNetworkProvider,

splunk-otel-android/src/main/java/com/splunk/rum/SplunkRum.java

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ public class SplunkRum {
6767

6868
static final AttributeKey<String> APP_NAME_KEY = stringKey("app");
6969
static final AttributeKey<String> RUM_VERSION_KEY = stringKey("splunk.rum.version");
70+
static final AttributeKey<String> APPLICATION_ID_KEY = stringKey("app.application.id");
71+
static final AttributeKey<String> APP_VERSION_CODE_KEY = stringKey("app.version.code");
72+
static final AttributeKey<String> SPLUNK_OLLY_UUID_KEY = stringKey("app.splunk.olly.uuid");
7073

7174
@Nullable private static SplunkRum INSTANCE;
7275

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.rum;
18+
19+
import static org.junit.Assert.*;
20+
import static org.mockito.Mockito.*;
21+
22+
import android.app.Application;
23+
import android.content.pm.ApplicationInfo;
24+
import android.content.pm.PackageInfo;
25+
import android.content.pm.PackageManager;
26+
import android.os.Bundle;
27+
import org.junit.Before;
28+
import org.junit.Test;
29+
import org.mockito.Mock;
30+
import org.mockito.MockitoAnnotations;
31+
32+
public class ErrorIdentifierExtractorTest {
33+
private static final String SPLUNK_UUID_MANIFEST_KEY = "SPLUNK_O11Y_CUSTOM_UUID";
34+
private static final String TEST_PACKAGE_NAME = "splunk.test.package.name";
35+
private static final String TEST_VERSION_CODE = "123";
36+
private static final String TEST_UUID = "test-uuid";
37+
38+
@Mock private Application mockApplication;
39+
@Mock private PackageManager mockPackageManager;
40+
@Mock private PackageInfo mockPackageInfo;
41+
@Mock private ApplicationInfo mockApplicationInfo;
42+
@Mock private Bundle mockMetadata;
43+
44+
@Before
45+
public void setUp() throws Exception {
46+
MockitoAnnotations.openMocks(this);
47+
48+
when(mockApplication.getApplicationContext()).thenReturn(mockApplication);
49+
when(mockApplication.getPackageManager()).thenReturn(mockPackageManager);
50+
when(mockApplication.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
51+
52+
mockApplicationInfo.packageName = TEST_PACKAGE_NAME;
53+
mockApplicationInfo.metaData = mockMetadata;
54+
55+
when(mockPackageManager.getApplicationInfo(TEST_PACKAGE_NAME, PackageManager.GET_META_DATA))
56+
.thenReturn(mockApplicationInfo);
57+
when(mockMetadata.getString(SPLUNK_UUID_MANIFEST_KEY)).thenReturn(TEST_UUID);
58+
59+
mockPackageInfo.versionCode = 123;
60+
when(mockPackageManager.getPackageInfo(TEST_PACKAGE_NAME, 0)).thenReturn(mockPackageInfo);
61+
}
62+
63+
@Test
64+
public void testGetApplicationId() {
65+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
66+
assertEquals(TEST_PACKAGE_NAME, extractor.extractInfo().getApplicationId());
67+
}
68+
69+
@Test
70+
public void testGetVersionCode() {
71+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
72+
assertEquals(TEST_VERSION_CODE, extractor.extractInfo().getVersionCode());
73+
}
74+
75+
@Test
76+
public void testGetCustomUUID() {
77+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
78+
assertEquals(TEST_UUID, extractor.extractInfo().getCustomUUID());
79+
}
80+
81+
@Test
82+
public void testCustomUUIDButDoesNotExist() {
83+
when(mockMetadata.getString(SPLUNK_UUID_MANIFEST_KEY)).thenReturn(null);
84+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
85+
assertNull(extractor.extractInfo().getCustomUUID());
86+
}
87+
88+
@Test
89+
public void testApplicationInfoMetaDataIsNull() throws PackageManager.NameNotFoundException {
90+
ApplicationInfo applicationInfoWithNullMetaData = new ApplicationInfo();
91+
applicationInfoWithNullMetaData.packageName = TEST_PACKAGE_NAME;
92+
93+
when(mockPackageManager.getApplicationInfo(TEST_PACKAGE_NAME, PackageManager.GET_META_DATA))
94+
.thenReturn(applicationInfoWithNullMetaData);
95+
96+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
97+
assertNull(extractor.extractInfo().getCustomUUID());
98+
}
99+
100+
@Test
101+
public void testRetrieveVersionCodeIsNull() throws PackageManager.NameNotFoundException {
102+
when(mockPackageManager.getPackageInfo(TEST_PACKAGE_NAME, 0))
103+
.thenThrow(new PackageManager.NameNotFoundException());
104+
105+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
106+
assertNull(extractor.extractInfo().getVersionCode());
107+
}
108+
109+
@Test
110+
public void testExtractInfoWhenApplicationInfoIsNull()
111+
throws PackageManager.NameNotFoundException {
112+
when(mockPackageManager.getApplicationInfo(TEST_PACKAGE_NAME, PackageManager.GET_META_DATA))
113+
.thenThrow(new PackageManager.NameNotFoundException());
114+
115+
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
116+
117+
ErrorIdentifierInfo info = extractor.extractInfo();
118+
assertNull(info.getApplicationId());
119+
assertEquals(TEST_VERSION_CODE, info.getVersionCode());
120+
assertNull(info.getCustomUUID());
121+
}
122+
}

0 commit comments

Comments
 (0)