Skip to content

Commit

Permalink
Added versionCode, applicationID, and customUUID from AndroidManifest…
Browse files Browse the repository at this point in the history
….XML (if it exists) to crash and ANR spans (#993) (#1002)
  • Loading branch information
tonzhan2 authored Oct 15, 2024
1 parent 8568998 commit a59c529
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright Splunk Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.splunk.rum;

import android.app.Application;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class ErrorIdentifierExtractor {

private static final String SPLUNK_UUID_MANIFEST_KEY = "SPLUNK_O11Y_CUSTOM_UUID";
private final Application application;
private final PackageManager packageManager;
@Nullable private final ApplicationInfo applicationInfo;

public ErrorIdentifierExtractor(@NonNull Application application) {
this.application = application;
this.packageManager = application.getPackageManager();
ApplicationInfo appInfo;
try {
appInfo =
packageManager.getApplicationInfo(
application.getPackageName(), PackageManager.GET_META_DATA);
} catch (Exception e) {
Log.e(
SplunkRum.LOG_TAG,
"Failed to initialize ErrorIdentifierExtractor: " + e.getMessage());
appInfo = null;
}
this.applicationInfo = appInfo;
}

public ErrorIdentifierInfo extractInfo() {
String applicationId = null;
String versionCode = retrieveVersionCode();
String customUUID = retrieveCustomUUID();

if (applicationInfo != null) {
applicationId = applicationInfo.packageName;
} else {
Log.e(SplunkRum.LOG_TAG, "ApplicationInfo is null, cannot extract applicationId");
}

return new ErrorIdentifierInfo(applicationId, versionCode, customUUID);
}

@Nullable
private String retrieveVersionCode() {
try {
PackageInfo packageInfo =
packageManager.getPackageInfo(application.getPackageName(), 0);
return String.valueOf(packageInfo.versionCode);
} catch (Exception e) {
Log.e(SplunkRum.LOG_TAG, "Failed to get application version code", e);
return null;
}
}

@Nullable
private String retrieveCustomUUID() {
if (applicationInfo == null) {
Log.e(SplunkRum.LOG_TAG, "ApplicationInfo is null; cannot retrieve Custom UUID.");
return null;
}
Bundle bundle = applicationInfo.metaData;
if (bundle != null) {
return bundle.getString(SPLUNK_UUID_MANIFEST_KEY);
} else {
Log.e(SplunkRum.LOG_TAG, "Application MetaData bundle is null");
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Splunk Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.splunk.rum;

import androidx.annotation.Nullable;

public class ErrorIdentifierInfo {
@Nullable private final String applicationId;
@Nullable private final String versionCode;
@Nullable private final String customUUID;

public ErrorIdentifierInfo(
@Nullable String applicationId,
@Nullable String versionCode,
@Nullable String customUUID) {
this.applicationId = applicationId;
this.versionCode = versionCode;
this.customUUID = customUUID;
}

@Nullable
public String getApplicationId() {
return applicationId;
}

@Nullable
public String getVersionCode() {
return versionCode;
}

@Nullable
public String getCustomUUID() {
return customUUID;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

package com.splunk.rum;

import static com.splunk.rum.SplunkRum.APPLICATION_ID_KEY;
import static com.splunk.rum.SplunkRum.APP_NAME_KEY;
import static com.splunk.rum.SplunkRum.APP_VERSION_CODE_KEY;
import static com.splunk.rum.SplunkRum.COMPONENT_APPSTART;
import static com.splunk.rum.SplunkRum.COMPONENT_ERROR;
import static com.splunk.rum.SplunkRum.COMPONENT_KEY;
import static com.splunk.rum.SplunkRum.COMPONENT_UI;
import static com.splunk.rum.SplunkRum.RUM_TRACER_NAME;
import static com.splunk.rum.SplunkRum.SPLUNK_OLLY_UUID_KEY;
import static io.opentelemetry.android.RumConstants.APP_START_SPAN_NAME;
import static io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor.constant;
import static io.opentelemetry.semconv.ResourceAttributes.DEPLOYMENT_ENVIRONMENT;
Expand All @@ -40,7 +43,9 @@
import io.opentelemetry.android.config.OtelRumConfig;
import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker;
import io.opentelemetry.android.instrumentation.anr.AnrDetector;
import io.opentelemetry.android.instrumentation.anr.AnrDetectorBuilder;
import io.opentelemetry.android.instrumentation.crash.CrashReporter;
import io.opentelemetry.android.instrumentation.crash.CrashReporterBuilder;
import io.opentelemetry.android.instrumentation.lifecycle.AndroidLifecycleInstrumentation;
import io.opentelemetry.android.instrumentation.network.CurrentNetworkProvider;
import io.opentelemetry.android.instrumentation.slowrendering.SlowRenderingDetector;
Expand Down Expand Up @@ -286,45 +291,70 @@ private Resource createSplunkResource() {
private void installAnrDetector(OpenTelemetryRumBuilder otelRumBuilder, Looper mainLooper) {
otelRumBuilder.addInstrumentation(
instrumentedApplication -> {
AnrDetector.builder()
.addAttributesExtractor(constant(COMPONENT_KEY, COMPONENT_ERROR))
.setMainLooper(mainLooper)
.build()
.installOn(instrumentedApplication);
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(application);
ErrorIdentifierInfo errorIdentifierInfo = extractor.extractInfo();
String applicationId = errorIdentifierInfo.getApplicationId();
String versionCode = errorIdentifierInfo.getVersionCode();
String uuid = errorIdentifierInfo.getCustomUUID();

initializationEvents.emit("anrMonitorInitialized");
});
}
AnrDetectorBuilder builder = AnrDetector.builder();
builder.addAttributesExtractor(constant(COMPONENT_KEY, COMPONENT_ERROR));

private void installSlowRenderingDetector(OpenTelemetryRumBuilder otelRumBuilder) {
otelRumBuilder.addInstrumentation(
instrumentedApplication -> {
SlowRenderingDetector.builder()
.setSlowRenderingDetectionPollInterval(
builder.slowRenderingDetectionPollInterval)
.build()
.installOn(instrumentedApplication);
initializationEvents.emit("slowRenderingDetectorInitialized");
if (applicationId != null)
builder.addAttributesExtractor(constant(APPLICATION_ID_KEY, applicationId));
if (versionCode != null)
builder.addAttributesExtractor(constant(APP_VERSION_CODE_KEY, versionCode));
if (uuid != null)
builder.addAttributesExtractor(constant(SPLUNK_OLLY_UUID_KEY, uuid));

builder.setMainLooper(mainLooper).build().installOn(instrumentedApplication);

initializationEvents.emit("anrMonitorInitialized");
});
}

private void installCrashReporter(OpenTelemetryRumBuilder otelRumBuilder) {
otelRumBuilder.addInstrumentation(
instrumentedApplication -> {
CrashReporter.builder()
.addAttributesExtractor(
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(application);
ErrorIdentifierInfo errorIdentifierInfo = extractor.extractInfo();
String applicationId = errorIdentifierInfo.getApplicationId();
String versionCode = errorIdentifierInfo.getVersionCode();
String uuid = errorIdentifierInfo.getCustomUUID();

CrashReporterBuilder builder = CrashReporter.builder();
builder.addAttributesExtractor(
RuntimeDetailsExtractor.create(
instrumentedApplication
.getApplication()
.getApplicationContext()))
.addAttributesExtractor(new CrashComponentExtractor())
.build()
.installOn(instrumentedApplication);
.addAttributesExtractor(new CrashComponentExtractor());

if (applicationId != null)
builder.addAttributesExtractor(constant(APPLICATION_ID_KEY, applicationId));
if (versionCode != null)
builder.addAttributesExtractor(constant(APP_VERSION_CODE_KEY, versionCode));
if (uuid != null)
builder.addAttributesExtractor(constant(SPLUNK_OLLY_UUID_KEY, uuid));

builder.build().installOn(instrumentedApplication);

initializationEvents.emit("crashReportingInitialized");
});
}

private void installSlowRenderingDetector(OpenTelemetryRumBuilder otelRumBuilder) {
otelRumBuilder.addInstrumentation(
instrumentedApplication -> {
SlowRenderingDetector.builder()
.setSlowRenderingDetectionPollInterval(
builder.slowRenderingDetectionPollInterval)
.build()
.installOn(instrumentedApplication);
initializationEvents.emit("slowRenderingDetectorInitialized");
});
}

// visible for testing
SpanExporter buildFilteringExporter(
CurrentNetworkProvider currentNetworkProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public class SplunkRum {

static final AttributeKey<String> APP_NAME_KEY = stringKey("app");
static final AttributeKey<String> RUM_VERSION_KEY = stringKey("splunk.rum.version");
static final AttributeKey<String> APPLICATION_ID_KEY = stringKey("app.application.id");
static final AttributeKey<String> APP_VERSION_CODE_KEY = stringKey("app.version.code");
static final AttributeKey<String> SPLUNK_OLLY_UUID_KEY = stringKey("app.splunk.olly.uuid");

@Nullable private static SplunkRum INSTANCE;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright Splunk Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.splunk.rum;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import android.app.Application;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class ErrorIdentifierExtractorTest {
private static final String SPLUNK_UUID_MANIFEST_KEY = "SPLUNK_O11Y_CUSTOM_UUID";
private static final String TEST_PACKAGE_NAME = "splunk.test.package.name";
private static final String TEST_VERSION_CODE = "123";
private static final String TEST_UUID = "test-uuid";

@Mock private Application mockApplication;
@Mock private PackageManager mockPackageManager;
@Mock private PackageInfo mockPackageInfo;
@Mock private ApplicationInfo mockApplicationInfo;
@Mock private Bundle mockMetadata;

@Before
public void setUp() throws Exception {
MockitoAnnotations.openMocks(this);

when(mockApplication.getApplicationContext()).thenReturn(mockApplication);
when(mockApplication.getPackageManager()).thenReturn(mockPackageManager);
when(mockApplication.getPackageName()).thenReturn(TEST_PACKAGE_NAME);

mockApplicationInfo.packageName = TEST_PACKAGE_NAME;
mockApplicationInfo.metaData = mockMetadata;

when(mockPackageManager.getApplicationInfo(TEST_PACKAGE_NAME, PackageManager.GET_META_DATA))
.thenReturn(mockApplicationInfo);
when(mockMetadata.getString(SPLUNK_UUID_MANIFEST_KEY)).thenReturn(TEST_UUID);

mockPackageInfo.versionCode = 123;
when(mockPackageManager.getPackageInfo(TEST_PACKAGE_NAME, 0)).thenReturn(mockPackageInfo);
}

@Test
public void testGetApplicationId() {
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
assertEquals(TEST_PACKAGE_NAME, extractor.extractInfo().getApplicationId());
}

@Test
public void testGetVersionCode() {
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
assertEquals(TEST_VERSION_CODE, extractor.extractInfo().getVersionCode());
}

@Test
public void testGetCustomUUID() {
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
assertEquals(TEST_UUID, extractor.extractInfo().getCustomUUID());
}

@Test
public void testCustomUUIDButDoesNotExist() {
when(mockMetadata.getString(SPLUNK_UUID_MANIFEST_KEY)).thenReturn(null);
ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
assertNull(extractor.extractInfo().getCustomUUID());
}

@Test
public void testApplicationInfoMetaDataIsNull() throws PackageManager.NameNotFoundException {
ApplicationInfo applicationInfoWithNullMetaData = new ApplicationInfo();
applicationInfoWithNullMetaData.packageName = TEST_PACKAGE_NAME;

when(mockPackageManager.getApplicationInfo(TEST_PACKAGE_NAME, PackageManager.GET_META_DATA))
.thenReturn(applicationInfoWithNullMetaData);

ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
assertNull(extractor.extractInfo().getCustomUUID());
}

@Test
public void testRetrieveVersionCodeIsNull() throws PackageManager.NameNotFoundException {
when(mockPackageManager.getPackageInfo(TEST_PACKAGE_NAME, 0))
.thenThrow(new PackageManager.NameNotFoundException());

ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);
assertNull(extractor.extractInfo().getVersionCode());
}

@Test
public void testExtractInfoWhenApplicationInfoIsNull()
throws PackageManager.NameNotFoundException {
when(mockPackageManager.getApplicationInfo(TEST_PACKAGE_NAME, PackageManager.GET_META_DATA))
.thenThrow(new PackageManager.NameNotFoundException());

ErrorIdentifierExtractor extractor = new ErrorIdentifierExtractor(mockApplication);

ErrorIdentifierInfo info = extractor.extractInfo();
assertNull(info.getApplicationId());
assertEquals(TEST_VERSION_CODE, info.getVersionCode());
assertNull(info.getCustomUUID());
}
}

0 comments on commit a59c529

Please sign in to comment.