diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/ApplicationInfoHelper.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/ApplicationInfoHelper.kt new file mode 100644 index 0000000000..907a2e3252 --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/ApplicationInfoHelper.kt @@ -0,0 +1,41 @@ +package com.onesignal + +import android.annotation.TargetApi +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.os.DeadSystemException +import android.util.AndroidException + +class ApplicationInfoHelper { + companion object { + // Safe to cache as nothing can change the app while it is running. + private var cachedInfo: ApplicationInfo? = null + + @TargetApi(24) + fun getInfo(context: Context): ApplicationInfo? { + if (cachedInfo != null) { + return cachedInfo + } + + val packageManager = context.packageManager + return try { + // Using this instead of context.applicationInfo as it's metaData is always null + cachedInfo = packageManager.getApplicationInfo( + context.packageName, + PackageManager.GET_META_DATA, + ) + cachedInfo + } catch (e: AndroidException) { + // Suppressing DeadSystemException as the app is already dying for + // another reason and allowing this exception to bubble up would + // create a red herring for app developers. We still re-throw + // others, as we don't want to silently hide other issues. + if (e !is DeadSystemException) { + throw e + } + null + } + } + } +} diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/BadgeCountUpdater.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/BadgeCountUpdater.java index 610ab81548..49aeef50cd 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/BadgeCountUpdater.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/BadgeCountUpdater.java @@ -29,7 +29,6 @@ import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.database.Cursor; import android.os.Build; import android.os.Bundle; @@ -51,19 +50,20 @@ private static boolean areBadgeSettingsEnabled(Context context) { if (badgesEnabled != -1) return (badgesEnabled == 1); - try { - ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = ai.metaData; - if (bundle != null) { - String defaultStr = bundle.getString("com.onesignal.BadgeCount"); - badgesEnabled = "DISABLE".equals(defaultStr) ? 0 : 1; - } - else - badgesEnabled = 1; - } catch (PackageManager.NameNotFoundException e) { + ApplicationInfo ai = ApplicationInfoHelper.Companion.getInfo(context); + if (ai == null) { badgesEnabled = 0; - OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Error reading meta-data tag 'com.onesignal.BadgeCount'. Disabling badge setting.", e); + OneSignal.Log(OneSignal.LOG_LEVEL.ERROR, "Error reading meta-data tag 'com.onesignal.BadgeCount'. Disabling badge setting."); + return false; + } + + Bundle bundle = ai.metaData; + if (bundle != null) { + String defaultStr = bundle.getString("com.onesignal.BadgeCount"); + badgesEnabled = "DISABLE".equals(defaultStr) ? 0 : 1; } + else + badgesEnabled = 1; return (badgesEnabled == 1); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotification.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotification.java index 289286c399..d68d27445f 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotification.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotification.java @@ -33,6 +33,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; @@ -133,6 +134,15 @@ static void isRunningOnMainThreadCheck() { if (OSUtils.isRunningOnMainThread()) throw new OSThrowable.OSMainThreadException("Process for showing a notification should never been done on Main Thread!"); } + + private static CharSequence getApplicationLabel() { + ApplicationInfo applicationInfo = ApplicationInfoHelper.Companion.getInfo(currentContext); + if (applicationInfo == null) { + return ""; + } + + return currentContext.getPackageManager().getApplicationLabel(applicationInfo); + } private static CharSequence getTitle(JSONObject fcmJson) { CharSequence title = fcmJson.optString("title", null); @@ -140,7 +150,7 @@ private static CharSequence getTitle(JSONObject fcmJson) { if (title != null) return title; - return currentContext.getPackageManager().getApplicationLabel(currentContext.getApplicationInfo()); + return getApplicationLabel(); } private static PendingIntent getNewDismissActionPendingIntent(int requestCode, Intent intent) { @@ -615,7 +625,7 @@ private static void createSummaryNotification(OSNotificationGenerationJob notifi // Default small and large icons are used instead of the payload options to enforce this. summaryBuilder.setContentIntent(summaryContentIntent) .setDeleteIntent(summaryDeleteIntent) - .setContentTitle(currentContext.getPackageManager().getApplicationLabel(currentContext.getApplicationInfo())) + .setContentTitle(getApplicationLabel()) .setContentText(summaryMessage) .setNumber(notificationCount) .setSmallIcon(getDefaultSmallIconId()) @@ -735,7 +745,7 @@ private static void createGrouplessSummaryNotification( // Default small and large icons are used instead of the payload options to enforce this. summaryBuilder.setContentIntent(summaryContentIntent) .setDeleteIntent(summaryDeleteIntent) - .setContentTitle(currentContext.getPackageManager().getApplicationLabel(currentContext.getApplicationInfo())) + .setContentTitle(getApplicationLabel()) .setContentText(summaryMessage) .setNumber(grouplessNotifCount) .setSmallIcon(getDefaultSmallIconId()) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GooglePlayServicesUpgradePrompt.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GooglePlayServicesUpgradePrompt.java index 32bd9f7cc9..4be0e57cc7 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/GooglePlayServicesUpgradePrompt.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/GooglePlayServicesUpgradePrompt.java @@ -4,7 +4,6 @@ import android.app.AlertDialog; import android.app.PendingIntent; import android.content.DialogInterface; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import com.google.android.gms.common.GoogleApiAvailability; @@ -13,18 +12,23 @@ class GooglePlayServicesUpgradePrompt { private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9_000; - private static boolean isGooglePlayStoreInstalled() { - try { - PackageManager pm = OneSignal.appContext.getPackageManager(); - PackageInfo info = pm.getPackageInfo(GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE, PackageManager.GET_META_DATA); - String label = (String) info.applicationInfo.loadLabel(pm); - return (!label.equals("Market")); - } catch (PackageManager.NameNotFoundException e) { - // Google Play Store might not be installed, ignore exception if so + GetPackageInfoResult result = + PackageInfoHelper.Companion.getInfo( + OneSignal.appContext, + GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE, + PackageManager.GET_META_DATA + ); + if (!result.getSuccessful()) { + return false; + } + if (result.getPackageInfo() == null) { + return false; } - return false; + PackageManager pm = OneSignal.appContext.getPackageManager(); + String label = (String) result.getPackageInfo().applicationInfo.loadLabel(pm); + return !label.equals("Market"); } static void showUpdateGPSDialog() { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/LocationController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/LocationController.java index 300b47b72f..c8a6436837 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/LocationController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/LocationController.java @@ -28,7 +28,6 @@ package com.onesignal; import android.content.Context; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.location.Location; import android.os.Build; @@ -221,48 +220,54 @@ static void getLocation(Context context, boolean promptLocation, boolean fallbac startGetLocation(); } else { // Android 6.0+ if (locationFinePermission != PackageManager.PERMISSION_GRANTED) { - try { - PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS); - List permissionList = Arrays.asList(packageInfo.requestedPermissions); - OneSignal.PromptActionResult result = OneSignal.PromptActionResult.PERMISSION_DENIED; + GetPackageInfoResult packageResult = + PackageInfoHelper.Companion.getInfo( + context, + context.getPackageName(), + PackageManager.GET_PERMISSIONS + ); + + if (!packageResult.getSuccessful() || packageResult.getPackageInfo() == null) { + sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.ERROR); + return; + } + + List permissionList = Arrays.asList(packageResult.getPackageInfo().requestedPermissions); + OneSignal.PromptActionResult result = OneSignal.PromptActionResult.PERMISSION_DENIED; - if (permissionList.contains("android.permission.ACCESS_FINE_LOCATION")) { - // ACCESS_FINE_LOCATION permission defined on Manifest, prompt for permission + if (permissionList.contains("android.permission.ACCESS_FINE_LOCATION")) { + // ACCESS_FINE_LOCATION permission defined on Manifest, prompt for permission + // If permission already given prompt will return positive, otherwise will prompt again or show settings + requestPermission = "android.permission.ACCESS_FINE_LOCATION"; + } else if (permissionList.contains("android.permission.ACCESS_COARSE_LOCATION")) { + if (locationCoarsePermission != PackageManager.PERMISSION_GRANTED) { + // ACCESS_COARSE_LOCATION permission defined on Manifest, prompt for permission // If permission already given prompt will return positive, otherwise will prompt again or show settings - requestPermission = "android.permission.ACCESS_FINE_LOCATION"; - } else if (permissionList.contains("android.permission.ACCESS_COARSE_LOCATION")) { - if (locationCoarsePermission != PackageManager.PERMISSION_GRANTED) { - // ACCESS_COARSE_LOCATION permission defined on Manifest, prompt for permission - // If permission already given prompt will return positive, otherwise will prompt again or show settings - requestPermission = "android.permission.ACCESS_COARSE_LOCATION"; - } else if (Build.VERSION.SDK_INT >= 29 && permissionList.contains("android.permission.ACCESS_BACKGROUND_LOCATION")) { - // ACCESS_BACKGROUND_LOCATION permission defined on Manifest, prompt for permission - requestPermission = "android.permission.ACCESS_BACKGROUND_LOCATION"; - } - } else { - OneSignal.onesignalLog(OneSignal.LOG_LEVEL.INFO, "Location permissions not added on AndroidManifest file"); - result = OneSignal.PromptActionResult.LOCATION_PERMISSIONS_MISSING_MANIFEST; + requestPermission = "android.permission.ACCESS_COARSE_LOCATION"; + } else if (Build.VERSION.SDK_INT >= 29 && permissionList.contains("android.permission.ACCESS_BACKGROUND_LOCATION")) { + // ACCESS_BACKGROUND_LOCATION permission defined on Manifest, prompt for permission + requestPermission = "android.permission.ACCESS_BACKGROUND_LOCATION"; } + } else { + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.INFO, "Location permissions not added on AndroidManifest file"); + result = OneSignal.PromptActionResult.LOCATION_PERMISSIONS_MISSING_MANIFEST; + } - // We handle the following cases: - // 1 - If needed and available then prompt for permissions - // - Request permission can be ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION - // 2 - If the permission were already granted then start getting location - // 3 - If permission wasn't granted then trigger fail flow - // - // For each case, we call the prompt handlers - if (requestPermission != null && promptLocation) { - LocationPermissionController.INSTANCE.prompt(fallbackToSettings, requestPermission); - } else if (locationCoarsePermission == PackageManager.PERMISSION_GRANTED) { - sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.PERMISSION_GRANTED); - startGetLocation(); - } else { - sendAndClearPromptHandlers(promptLocation, result); - fireFailedComplete(); - } - } catch (PackageManager.NameNotFoundException e) { - sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.ERROR); - e.printStackTrace(); + // We handle the following cases: + // 1 - If needed and available then prompt for permissions + // - Request permission can be ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION + // 2 - If the permission were already granted then start getting location + // 3 - If permission wasn't granted then trigger fail flow + // + // For each case, we call the prompt handlers + if (requestPermission != null && promptLocation) { + LocationPermissionController.INSTANCE.prompt(fallbackToSettings, requestPermission); + } else if (locationCoarsePermission == PackageManager.PERMISSION_GRANTED) { + sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.PERMISSION_GRANTED); + startGetLocation(); + } else { + sendAndClearPromptHandlers(promptLocation, result); + fireFailedComplete(); } } else if (Build.VERSION.SDK_INT >= 29 && locationBackgroundPermission != PackageManager.PERMISSION_GRANTED) { backgroundLocationPermissionLogic(context, promptLocation, fallbackToSettings); @@ -279,25 +284,31 @@ static void getLocation(Context context, boolean promptLocation, boolean fallbac * If background permission is asked at the same time as fine and coarse then both permission request are ignored * */ private static void backgroundLocationPermissionLogic(Context context, boolean promptLocation, boolean fallbackToSettings) { - try { - PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS); - List permissionList = Arrays.asList(packageInfo.requestedPermissions); + GetPackageInfoResult result = + PackageInfoHelper.Companion.getInfo( + context, + context.getPackageName(), + PackageManager.GET_PERMISSIONS + ); + + if (!result.getSuccessful() || result.getPackageInfo() == null) { + sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.ERROR); + return; + } - if (permissionList.contains("android.permission.ACCESS_BACKGROUND_LOCATION")) { - // ACCESS_BACKGROUND_LOCATION permission defined on Manifest, prompt for permission - requestPermission = "android.permission.ACCESS_BACKGROUND_LOCATION"; - } + List permissionList = Arrays.asList(result.getPackageInfo().requestedPermissions); - if (requestPermission != null && promptLocation) { - LocationPermissionController.INSTANCE.prompt(fallbackToSettings, requestPermission); - } else { - // Fine permission already granted - sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.PERMISSION_GRANTED); - startGetLocation(); - } - } catch (PackageManager.NameNotFoundException e) { - sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.ERROR); - e.printStackTrace(); + if (permissionList.contains("android.permission.ACCESS_BACKGROUND_LOCATION")) { + // ACCESS_BACKGROUND_LOCATION permission defined on Manifest, prompt for permission + requestPermission = "android.permission.ACCESS_BACKGROUND_LOCATION"; + } + + if (requestPermission != null && promptLocation) { + LocationPermissionController.INSTANCE.prompt(fallbackToSettings, requestPermission); + } else { + // Fine permission already granted + sendAndClearPromptHandlers(promptLocation, OneSignal.PromptActionResult.PERMISSION_GRANTED); + startGetLocation(); } } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NavigateToAndroidSettingsForNotifications.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NavigateToAndroidSettingsForNotifications.kt index 157fe59e8c..e8798ad480 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NavigateToAndroidSettingsForNotifications.kt +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NavigateToAndroidSettingsForNotifications.kt @@ -38,7 +38,10 @@ object NavigateToAndroidSettingsForNotifications { // for Android 5-7 intent.putExtra("app_package", context.getPackageName()) - intent.putExtra("app_uid", context.getApplicationInfo().uid) + val applicationInfo = ApplicationInfoHelper.getInfo(context) + if (applicationInfo != null) { + intent.putExtra("app_uid", applicationInfo.uid) + } // for Android 8 and above intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationChannelManager.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationChannelManager.java index 57307a85f5..ab0ad74409 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationChannelManager.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationChannelManager.java @@ -34,6 +34,7 @@ import android.content.Context; import android.net.Uri; import android.os.Build; +import android.os.DeadSystemException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -47,7 +48,6 @@ import org.json.JSONObject; import java.math.BigInteger; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -239,14 +239,9 @@ static void processChannelList(@NonNull Context context, @Nullable JSONArray lis if (syncedChannelSet.isEmpty()) return; - List existingChannels = new ArrayList<>(); - - try { - existingChannels = notificationManager.getNotificationChannels(); - } catch (NullPointerException e) { - // Catch issue caused by "Attempt to invoke virtual method 'boolean android.app.NotificationChannel.isDeleted()' on a null object reference" - // https://github.com/OneSignal/OneSignal-Android-SDK/issues/1291 - OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Error when trying to delete notification channel: " + e.getMessage()); + List existingChannels = getChannelList(notificationManager); + if (existingChannels == null) { + return; } // Delete old channels - Payload will include all changes for the app. Any extra OS_ ones must @@ -257,7 +252,29 @@ static void processChannelList(@NonNull Context context, @Nullable JSONArray lis notificationManager.deleteNotificationChannel(id); } } - + + @RequiresApi(api = Build.VERSION_CODES.O) + private static List getChannelList(NotificationManager notificationManager) { + try { + return notificationManager.getNotificationChannels(); + } catch (NullPointerException e) { + // Catching known Android bug, Sometimes throws, + // "Attempt to invoke virtual method 'boolean android.app.NotificationChannel.isDeleted()' on a null object reference" + // https://github.com/OneSignal/OneSignal-Android-SDK/issues/1291 + OneSignal.onesignalLog(OneSignal.LOG_LEVEL.ERROR, "Error when trying to delete notification channel: " + e.getMessage()); + } + catch (Exception e) { + // Suppressing DeadSystemException as the app is already dying for + // another reason and allowing this exception to bubble up would + // create a red herring for app developers. We still re-throw + // others, as we don't want to silently hide other issues. + if (!(e instanceof DeadSystemException)) { + throw e; + } + } + return null; + } + private static int priorityToImportance(int priority) { if (priority > 9) return NotificationManagerCompat.IMPORTANCE_MAX; diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSUtils.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSUtils.java index 6f422d87e7..33e5b94105 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSUtils.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OSUtils.java @@ -27,6 +27,7 @@ package com.onesignal; +import android.annotation.TargetApi; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; @@ -40,6 +41,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.DeadSystemException; import android.os.Handler; import android.os.Looper; @@ -271,13 +273,22 @@ && getTargetSdkVersion(context) >= Build.VERSION_CODES.O) { } private static boolean packageInstalledAndEnabled(@NonNull String packageName) { - try { - PackageManager pm = OneSignal.appContext.getPackageManager(); - PackageInfo info = pm.getPackageInfo(packageName, PackageManager.GET_META_DATA); - return info.applicationInfo.enabled; - } catch (PackageManager.NameNotFoundException e) { + GetPackageInfoResult result = + PackageInfoHelper.Companion.getInfo( + OneSignal.appContext, + packageName, + PackageManager.GET_META_DATA + ); + if (!result.getSuccessful()) { + return false; + } + + PackageInfo info = result.getPackageInfo(); + if (info == null) { return false; } + + return info.applicationInfo.enabled; } // TODO: Maybe able to switch to GoogleApiAvailability.isGooglePlayServicesAvailable to simplify @@ -289,9 +300,21 @@ static boolean isGMSInstalledAndEnabled() { } private static final int HMS_AVAILABLE_SUCCESSFUL = 0; + @TargetApi(24) private static boolean isHMSCoreInstalledAndEnabled() { HuaweiApiAvailability availability = HuaweiApiAvailability.getInstance(); - return availability.isHuaweiMobileServicesAvailable(OneSignal.appContext) == HMS_AVAILABLE_SUCCESSFUL; + try { + return availability.isHuaweiMobileServicesAvailable(OneSignal.appContext) == HMS_AVAILABLE_SUCCESSFUL; + } catch (Exception e) { + // Suppressing DeadSystemException as the app is already dying for + // another reason and allowing this exception to bubble up would + // create a red herring for app developers. We still re-throw + // others, as we don't want to silently hide other issues. + if (!(e instanceof DeadSystemException)) { + throw e; + } + return false; + } } private static final String HMS_CORE_SERVICES_PACKAGE = "com.huawei.hwid"; // = HuaweiApiAvailability.SERVICES_PACKAGE @@ -407,15 +430,11 @@ String getCarrierName() { } static Bundle getManifestMetaBundle(Context context) { - ApplicationInfo ai; - try { - ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); - return ai.metaData; - } catch (PackageManager.NameNotFoundException e) { - Log(OneSignal.LOG_LEVEL.ERROR, "Manifest application info not found", e); + ApplicationInfo ai = ApplicationInfoHelper.Companion.getInfo(context); + if (ai == null) { + return null; } - - return null; + return ai.metaData; } static boolean getManifestMetaBoolean(Context context, String metaName) { @@ -487,16 +506,11 @@ static void runOnMainThreadDelayed(Runnable runnable, int delay) { } static int getTargetSdkVersion(Context context) { - String packageName = context.getPackageName(); - PackageManager packageManager = context.getPackageManager(); - try { - ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0); - return applicationInfo.targetSdkVersion; - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + ApplicationInfo applicationInfo = ApplicationInfoHelper.Companion.getInfo(context); + if (applicationInfo == null) { + return Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1; } - - return Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1; + return applicationInfo.targetSdkVersion; } static boolean isValidResourceName(String name) { diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java index 285e028daf..3bea5ebc55 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/OneSignal.java @@ -921,17 +921,16 @@ private static void setupContextListeners(boolean wasAppContextNull) { } private static void setupPrivacyConsent(Context context) { - try { - ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = ai.metaData; - - // Read the current privacy consent setting from AndroidManifest.xml - String requireSetting = bundle.getString("com.onesignal.PrivacyConsent"); - if (requireSetting != null) - setRequiresUserPrivacyConsent("ENABLE".equalsIgnoreCase(requireSetting)); - } catch (Throwable t) { - t.printStackTrace(); + ApplicationInfo ai = ApplicationInfoHelper.Companion.getInfo(context); + if (ai == null) { + return; } + Bundle bundle = ai.metaData; + + // Read the current privacy consent setting from AndroidManifest.xml + String requireSetting = bundle.getString("com.onesignal.PrivacyConsent"); + if (requireSetting != null) + setRequiresUserPrivacyConsent("ENABLE".equalsIgnoreCase(requireSetting)); } private static void handleAppIdChange() { @@ -1502,7 +1501,6 @@ public void run() { private static void registerUserTask() throws JSONException { String packageName = appContext.getPackageName(); - PackageManager packageManager = appContext.getPackageManager(); JSONObject deviceInfo = new JSONObject(); @@ -1516,11 +1514,10 @@ private static void registerUserTask() throws JSONException { deviceInfo.put("sdk_type", sdkType); deviceInfo.put("android_package", packageName); deviceInfo.put("device_model", Build.MODEL); - - try { - deviceInfo.put("game_version", packageManager.getPackageInfo(packageName, 0).versionCode); - } catch (PackageManager.NameNotFoundException e) {} - + Integer appVersion = getAppVersion(); + if (appVersion != null) { + deviceInfo.put("game_version", appVersion); + } deviceInfo.put("net_type", osUtils.getNetType()); deviceInfo.put("carrier", osUtils.getCarrierName()); deviceInfo.put("rooted", RootToolsInternalMethods.isRooted()); @@ -1543,6 +1540,21 @@ private static void registerUserTask() throws JSONException { waitingToPostStateSync = false; } + private static Integer getAppVersion() { + String packageName = appContext.getPackageName(); + GetPackageInfoResult result = + PackageInfoHelper.Companion.getInfo( + OneSignal.appContext, + packageName, + 0 + ); + if (!result.getSuccessful() || result.getPackageInfo() == null) { + return null; + } + + return result.getPackageInfo().versionCode; + } + public static void setSMSNumber(@NonNull final String smsNumber, OSSMSUpdateHandler callback) { setSMSNumber(smsNumber, null, callback); } diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/PackageInfoHelper.kt b/OneSignalSDK/onesignal/src/main/java/com/onesignal/PackageInfoHelper.kt new file mode 100644 index 0000000000..1645d28559 --- /dev/null +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/PackageInfoHelper.kt @@ -0,0 +1,50 @@ +package com.onesignal + +import android.annotation.TargetApi +import android.content.Context +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.os.DeadSystemException +import android.util.AndroidException + +data class GetPackageInfoResult( + // Check this value first, if false ignore other properties + // - If false this means Android throw an error, so it's not possible + // to know if the app we are checking is even installed. + val successful: Boolean, + + // Raw PackageInfo from Android API + // NOTE: Ignore this value if successful == false + // Will be null if package is not installed. + val packageInfo: PackageInfo?, +) + +class PackageInfoHelper { + companion object { + @TargetApi(24) + fun getInfo(appContext: Context, packageName: String, flags: Int): GetPackageInfoResult { + val packageManager = appContext.packageManager + return try { + GetPackageInfoResult( + true, + packageManager.getPackageInfo( + packageName, + flags, + ), + ) + } catch (e: PackageManager.NameNotFoundException) { + // Expected if package is not installed on the device. + GetPackageInfoResult(true, null) + } catch (e: AndroidException) { + // Suppressing DeadSystemException as the app is already dying for + // another reason and allowing this exception to bubble up would + // create a red herring for app developers. We still re-throw + // others, as we don't want to silently hide other issues. + if (e !is DeadSystemException) { + throw e + } + GetPackageInfoResult(false, null) + } + } + } +}