Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added versionCode, applicationID, and customUUID from AndroidManifest… #1002

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
breedx-splk marked this conversation as resolved.
Show resolved Hide resolved

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");
Comment on lines +70 to +72
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure I've said it before, but it would be lovely to see these added to the official otel semconv.


@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());
}
}
Loading