Skip to content

Commit b79b57c

Browse files
ganadistromtsn
andauthored
Add Split Apks info as extras (#3193)
* Add Split Apks info as tags * Disable to get splitNames for Kitkat or below * Use extras instead of tags * Add testcase for splitapk info * Send split apks info as part of App context * Changelog * Remove redundant method --------- Co-authored-by: Roman Zavarnitsyn <[email protected]>
1 parent 91474b9 commit b79b57c

File tree

10 files changed

+175
-4
lines changed

10 files changed

+175
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Features
1010

1111
- Create onCreate and onStart spans for all Activities ([#4025](https://github.com/getsentry/sentry-java/pull/4025))
12+
- Add split apks info to the `App` context ([#3193](https://github.com/getsentry/sentry-java/pull/3193)))
1213

1314
### Fixes
1415

sentry-android-core/api/sentry-android-core.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ public final class io/sentry/android/core/DeviceInfoUtil {
195195
public static fun getInstance (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;)Lio/sentry/android/core/DeviceInfoUtil;
196196
public fun getOperatingSystem ()Lio/sentry/protocol/OperatingSystem;
197197
public fun getSideLoadedInfo ()Lio/sentry/android/core/ContextUtils$SideLoadedInfo;
198+
public fun getSplitApksInfo ()Lio/sentry/android/core/ContextUtils$SplitApksInfo;
198199
public fun getTotalMemory ()Ljava/lang/Long;
199200
public static fun isCharging (Landroid/content/Intent;Lio/sentry/SentryOptions;)Ljava/lang/Boolean;
200201
public static fun resetInstance ()V

sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,19 @@ private void setApp(final @NotNull SentryBaseEvent event, final @NotNull Object
404404
}
405405
}
406406

407+
try {
408+
final ContextUtils.SplitApksInfo splitApksInfo =
409+
DeviceInfoUtil.getInstance(context, options).getSplitApksInfo();
410+
if (splitApksInfo != null) {
411+
app.setSplitApks(splitApksInfo.isSplitApks());
412+
if (splitApksInfo.getSplitNames() != null) {
413+
app.setSplitNames(Arrays.asList(splitApksInfo.getSplitNames()));
414+
}
415+
}
416+
} catch (Throwable e) {
417+
options.getLogger().log(SentryLevel.ERROR, "Error getting split apks info.", e);
418+
}
419+
407420
event.getContexts().setApp(app);
408421
}
409422

sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.io.File;
2727
import java.io.FileReader;
2828
import java.io.IOException;
29+
import java.util.Arrays;
2930
import java.util.HashMap;
3031
import java.util.Map;
3132
import org.jetbrains.annotations.ApiStatus;
@@ -63,6 +64,27 @@ public boolean isSideLoaded() {
6364
}
6465
}
6566

67+
static class SplitApksInfo {
68+
// https://github.com/google/bundletool/blob/master/src/main/java/com/android/tools/build/bundletool/model/AndroidManifest.java#L257-L263
69+
static final String SPLITS_REQUIRED = "com.android.vending.splits.required";
70+
71+
private final boolean isSplitApks;
72+
private final String[] splitNames;
73+
74+
public SplitApksInfo(final boolean isSplitApks, final String[] splitNames) {
75+
this.isSplitApks = isSplitApks;
76+
this.splitNames = splitNames;
77+
}
78+
79+
public boolean isSplitApks() {
80+
return isSplitApks;
81+
}
82+
83+
public @Nullable String[] getSplitNames() {
84+
return splitNames;
85+
}
86+
}
87+
6688
private ContextUtils() {}
6789

6890
// to avoid doing a bunch of Binder calls we use LazyEvaluator to cache the values that are static
@@ -322,6 +344,26 @@ public static boolean isForegroundImportance() {
322344
return null;
323345
}
324346

347+
@SuppressWarnings({"deprecation"})
348+
static @Nullable SplitApksInfo retrieveSplitApksInfo(
349+
final @NotNull Context context, final @NotNull BuildInfoProvider buildInfoProvider) {
350+
String[] splitNames = null;
351+
final ApplicationInfo applicationInfo = getApplicationInfo(context, buildInfoProvider);
352+
final PackageInfo packageInfo = getPackageInfo(context, buildInfoProvider);
353+
354+
if (packageInfo != null) {
355+
splitNames = packageInfo.splitNames;
356+
boolean isSplitApks = false;
357+
if (applicationInfo != null && applicationInfo.metaData != null) {
358+
isSplitApks = applicationInfo.metaData.getBoolean(SplitApksInfo.SPLITS_REQUIRED);
359+
}
360+
361+
return new SplitApksInfo(isSplitApks, splitNames);
362+
}
363+
364+
return null;
365+
}
366+
325367
/**
326368
* Get the human-facing Application name.
327369
*
@@ -422,6 +464,7 @@ public static boolean isForegroundImportance() {
422464
static void setAppPackageInfo(
423465
final @NotNull PackageInfo packageInfo,
424466
final @NotNull BuildInfoProvider buildInfoProvider,
467+
final @Nullable DeviceInfoUtil deviceInfoUtil,
425468
final @NotNull App app) {
426469
app.setAppIdentifier(packageInfo.packageName);
427470
app.setAppVersion(packageInfo.versionName);
@@ -446,6 +489,19 @@ static void setAppPackageInfo(
446489
}
447490
}
448491
app.setPermissions(permissions);
492+
493+
if (deviceInfoUtil != null) {
494+
try {
495+
final ContextUtils.SplitApksInfo splitApksInfo = deviceInfoUtil.getSplitApksInfo();
496+
if (splitApksInfo != null) {
497+
app.setSplitApks(splitApksInfo.isSplitApks());
498+
if (splitApksInfo.getSplitNames() != null) {
499+
app.setSplitNames(Arrays.asList(splitApksInfo.getSplitNames()));
500+
}
501+
}
502+
} catch (Throwable e) {
503+
}
504+
}
449505
}
450506

451507
/**

sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,15 @@ private void setPackageInfo(final @NotNull SentryBaseEvent event, final @NotNull
239239
String versionCode = ContextUtils.getVersionCode(packageInfo, buildInfoProvider);
240240

241241
setDist(event, versionCode);
242-
ContextUtils.setAppPackageInfo(packageInfo, buildInfoProvider, app);
242+
243+
@Nullable DeviceInfoUtil deviceInfoUtil = null;
244+
try {
245+
deviceInfoUtil = this.deviceInfoUtil.get();
246+
} catch (Throwable e) {
247+
options.getLogger().log(SentryLevel.ERROR, "Failed to retrieve device info", e);
248+
}
249+
250+
ContextUtils.setAppPackageInfo(packageInfo, buildInfoProvider, deviceInfoUtil, app);
243251
}
244252
}
245253

sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public final class DeviceInfoUtil {
4949
private final @NotNull BuildInfoProvider buildInfoProvider;
5050
private final @Nullable Boolean isEmulator;
5151
private final @Nullable ContextUtils.SideLoadedInfo sideLoadedInfo;
52+
private final @Nullable ContextUtils.SplitApksInfo splitApksInfo;
5253
private final @NotNull OperatingSystem os;
5354

5455
private final @Nullable Long totalMem;
@@ -65,6 +66,7 @@ public DeviceInfoUtil(
6566
isEmulator = buildInfoProvider.isEmulator();
6667
sideLoadedInfo =
6768
ContextUtils.retrieveSideLoadedInfo(context, options.getLogger(), buildInfoProvider);
69+
splitApksInfo = ContextUtils.retrieveSplitApksInfo(context, buildInfoProvider);
6870
final @Nullable ActivityManager.MemoryInfo memInfo =
6971
ContextUtils.getMemInfo(context, options.getLogger());
7072
if (memInfo != null) {
@@ -188,6 +190,11 @@ public ContextUtils.SideLoadedInfo getSideLoadedInfo() {
188190
return sideLoadedInfo;
189191
}
190192

193+
@Nullable
194+
public ContextUtils.SplitApksInfo getSplitApksInfo() {
195+
return splitApksInfo;
196+
}
197+
191198
private void setDeviceIO(final @NotNull Device device, final boolean includeDynamicData) {
192199
final Intent batteryIntent = getBatteryIntent();
193200
if (batteryIntent != null) {

sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public static Map<String, Object> serializeScope(
124124
ContextUtils.getPackageInfo(
125125
context, PackageManager.GET_PERMISSIONS, options.getLogger(), buildInfoProvider);
126126
if (packageInfo != null) {
127-
ContextUtils.setAppPackageInfo(packageInfo, buildInfoProvider, app);
127+
ContextUtils.setAppPackageInfo(packageInfo, buildInfoProvider, deviceInfoUtil, app);
128128
}
129129
scope.getContexts().setApp(app);
130130

sentry-android-core/src/test/java/io/sentry/android/core/ContextUtilsTest.kt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import android.app.ActivityManager.RunningAppProcessInfo
77
import android.content.BroadcastReceiver
88
import android.content.Context
99
import android.content.IntentFilter
10+
import android.content.pm.ApplicationInfo
11+
import android.content.pm.PackageInfo
12+
import android.content.pm.PackageManager
1013
import android.os.Build
1114
import android.os.Process
1215
import androidx.test.core.app.ApplicationProvider
@@ -26,6 +29,7 @@ import org.robolectric.shadows.ShadowActivityManager
2629
import org.robolectric.shadows.ShadowBuild
2730
import kotlin.test.BeforeTest
2831
import kotlin.test.Test
32+
import kotlin.test.assertContentEquals
2933
import kotlin.test.assertEquals
3034
import kotlin.test.assertFalse
3135
import kotlin.test.assertNotNull
@@ -116,6 +120,38 @@ class ContextUtilsTest {
116120
assertEquals("play.google.com", sideLoadedInfo.installerStore)
117121
}
118122

123+
@Test
124+
fun `given a valid PackageInfo, returns valid splitNames`() {
125+
val splitNames = arrayOf<String?>("config.arm64_v8a")
126+
val mockedContext = mock<Context>()
127+
val mockedPackageManager = mock<PackageManager>()
128+
val mockedApplicationInfo = mock<ApplicationInfo>()
129+
val mockedPackageInfo = mock<PackageInfo>()
130+
mockedPackageInfo.splitNames = splitNames
131+
132+
whenever(mockedContext.packageName).thenReturn("dummy")
133+
134+
whenever(
135+
mockedPackageManager.getApplicationInfo(
136+
any<String>(),
137+
any<PackageManager.ApplicationInfoFlags>()
138+
)
139+
).thenReturn(mockedApplicationInfo)
140+
141+
whenever(
142+
mockedPackageManager.getPackageInfo(
143+
any<String>(),
144+
any<PackageManager.PackageInfoFlags>()
145+
)
146+
).thenReturn(mockedPackageInfo)
147+
148+
whenever(mockedContext.packageManager).thenReturn(mockedPackageManager)
149+
150+
val splitApksInfo =
151+
ContextUtils.retrieveSplitApksInfo(mockedContext, BuildInfoProvider(logger))
152+
assertContentEquals(splitNames, splitApksInfo!!.splitNames)
153+
}
154+
119155
@Test
120156
@Config(qualifiers = "w360dp-h640dp-xxhdpi")
121157
fun `when display metrics specified, getDisplayMetrics returns correct values`() {

sentry/api/sentry.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4333,6 +4333,8 @@ public final class io/sentry/protocol/App : io/sentry/JsonSerializable, io/sentr
43334333
public fun getDeviceAppHash ()Ljava/lang/String;
43344334
public fun getInForeground ()Ljava/lang/Boolean;
43354335
public fun getPermissions ()Ljava/util/Map;
4336+
public fun getSplitApks ()Ljava/lang/Boolean;
4337+
public fun getSplitNames ()Ljava/util/List;
43364338
public fun getStartType ()Ljava/lang/String;
43374339
public fun getUnknown ()Ljava/util/Map;
43384340
public fun getViewNames ()Ljava/util/List;
@@ -4347,6 +4349,8 @@ public final class io/sentry/protocol/App : io/sentry/JsonSerializable, io/sentr
43474349
public fun setDeviceAppHash (Ljava/lang/String;)V
43484350
public fun setInForeground (Ljava/lang/Boolean;)V
43494351
public fun setPermissions (Ljava/util/Map;)V
4352+
public fun setSplitApks (Ljava/lang/Boolean;)V
4353+
public fun setSplitNames (Ljava/util/List;)V
43504354
public fun setStartType (Ljava/lang/String;)V
43514355
public fun setUnknown (Ljava/util/Map;)V
43524356
public fun setViewNames (Ljava/util/List;)V
@@ -4368,6 +4372,8 @@ public final class io/sentry/protocol/App$JsonKeys {
43684372
public static final field BUILD_TYPE Ljava/lang/String;
43694373
public static final field DEVICE_APP_HASH Ljava/lang/String;
43704374
public static final field IN_FOREGROUND Ljava/lang/String;
4375+
public static final field IS_SPLIT_APKS Ljava/lang/String;
4376+
public static final field SPLIT_NAMES Ljava/lang/String;
43714377
public static final field START_TYPE Ljava/lang/String;
43724378
public static final field VIEW_NAMES Ljava/lang/String;
43734379
public fun <init> ()V

sentry/src/main/java/io/sentry/protocol/App.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public final class App implements JsonUnknown, JsonSerializable {
4949
* visible to the user.
5050
*/
5151
private @Nullable Boolean inForeground;
52+
/** A flag indicating whether the app is split into multiple APKs */
53+
private @Nullable Boolean isSplitApks;
54+
/* The list of split APKs */
55+
private @Nullable List<String> splitNames;
5256

5357
public App() {}
5458

@@ -64,6 +68,8 @@ public App() {}
6468
this.inForeground = app.inForeground;
6569
this.viewNames = CollectionUtils.newArrayList(app.viewNames);
6670
this.startType = app.startType;
71+
this.isSplitApks = app.isSplitApks;
72+
this.splitNames = app.splitNames;
6773
this.unknown = CollectionUtils.newConcurrentHashMap(app.unknown);
6874
}
6975

@@ -163,6 +169,22 @@ public void setStartType(final @Nullable String startType) {
163169
this.startType = startType;
164170
}
165171

172+
public @Nullable Boolean getSplitApks() {
173+
return isSplitApks;
174+
}
175+
176+
public void setSplitApks(final @Nullable Boolean splitApks) {
177+
isSplitApks = splitApks;
178+
}
179+
180+
public @Nullable List<String> getSplitNames() {
181+
return splitNames;
182+
}
183+
184+
public void setSplitNames(final @Nullable List<String> splitNames) {
185+
this.splitNames = splitNames;
186+
}
187+
166188
@Override
167189
public boolean equals(Object o) {
168190
if (this == o) return true;
@@ -178,7 +200,9 @@ public boolean equals(Object o) {
178200
&& Objects.equals(permissions, app.permissions)
179201
&& Objects.equals(inForeground, app.inForeground)
180202
&& Objects.equals(viewNames, app.viewNames)
181-
&& Objects.equals(startType, app.startType);
203+
&& Objects.equals(startType, app.startType)
204+
&& Objects.equals(isSplitApks, app.isSplitApks)
205+
&& Objects.equals(splitNames, app.splitNames);
182206
}
183207

184208
@Override
@@ -194,7 +218,9 @@ public int hashCode() {
194218
permissions,
195219
inForeground,
196220
viewNames,
197-
startType);
221+
startType,
222+
isSplitApks,
223+
splitNames);
198224
}
199225

200226
// region json
@@ -222,6 +248,8 @@ public static final class JsonKeys {
222248
public static final String IN_FOREGROUND = "in_foreground";
223249
public static final String VIEW_NAMES = "view_names";
224250
public static final String START_TYPE = "start_type";
251+
public static final String IS_SPLIT_APKS = "is_split_apks";
252+
public static final String SPLIT_NAMES = "split_names";
225253
}
226254

227255
@Override
@@ -261,6 +289,12 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger
261289
if (startType != null) {
262290
writer.name(JsonKeys.START_TYPE).value(startType);
263291
}
292+
if (isSplitApks != null) {
293+
writer.name(JsonKeys.IS_SPLIT_APKS).value(isSplitApks);
294+
}
295+
if (splitNames != null && !splitNames.isEmpty()) {
296+
writer.name(JsonKeys.SPLIT_NAMES).value(logger, splitNames);
297+
}
264298
if (unknown != null) {
265299
for (String key : unknown.keySet()) {
266300
Object value = unknown.get(key);
@@ -319,6 +353,15 @@ public static final class Deserializer implements JsonDeserializer<App> {
319353
case JsonKeys.START_TYPE:
320354
app.startType = reader.nextStringOrNull();
321355
break;
356+
case JsonKeys.IS_SPLIT_APKS:
357+
app.isSplitApks = reader.nextBooleanOrNull();
358+
break;
359+
case JsonKeys.SPLIT_NAMES:
360+
final @Nullable List<String> splitNames = (List<String>) reader.nextObjectOrNull();
361+
if (splitNames != null) {
362+
app.setSplitNames(splitNames);
363+
}
364+
break;
322365
default:
323366
if (unknown == null) {
324367
unknown = new ConcurrentHashMap<>();

0 commit comments

Comments
 (0)