microsoft / appcenter-sdk-android

Development repository for the App Center SDK for Android
Other
277 stars 134 forks source link

Crash when direct boot #1599

Closed vvb2060 closed 2 years ago

vvb2060 commented 2 years ago

Description

[ 2022-02-13T10:30:39.144    11471:  3145:  3347 E/AndroidRuntime  ] FATAL EXCEPTION: AppCenter.Looper
Process: [edited], PID: 3145
java.lang.IllegalStateException: SharedPreferences in credential encrypted storage are not available until after user is unlocked
 at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:494)
 at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:479)
 at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:188)
 at com.microsoft.appcenter.utils.storage.SharedPreferencesManager.initialize(SharedPreferencesManager.java:45)
 at com.microsoft.appcenter.AppCenter.finishConfiguration(AppCenter.java:832)
 at com.microsoft.appcenter.AppCenter.access$500(AppCenter.java:61)
 at com.microsoft.appcenter.AppCenter$5.run(AppCenter.java:737)
 at android.os.Handler.handleCallback(Handler.java:938)
 at android.os.Handler.dispatchMessage(Handler.java:99)
 at android.os.Looper.loop(Looper.java:223)
 at android.os.HandlerThread.run(HandlerThread.java:67)

Repro Steps

Please list the steps used to reproduce your issue.

  1. Add AppCenter.start() when Application.onCreate()
  2. add receiver
        <receiver
            android:name=".BootReceiver"
            android:exported="true"
            android:directBootAware="true">
            <intent-filter>
                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
  3. reboot and logcat -b crash

Details

  1. Which SDK version are you using? 4.4.2
  2. Which OS version did you experience the issue on? Android 7.0+
  3. What device version did you see this error on? Were you using an emulator or a physical device? All Android 7.0+ FBE devices

https://developer.android.com/training/articles/direct-boot

AnatolyPristensky commented 2 years ago

Hello @vvb2060 And thank you for getting in touch with us! I'm working on your issue. Will update this conversation once get results.

AnatolyPristensky commented 2 years ago

@vvb2060 , Unfortunately, I'm unable to reproduce your issue for now. It will be great if you'll have a possibility to share sample app reproducing the issue.

vvb2060 commented 2 years ago

No need to reproduce, just follow this guide https://developer.android.com/training/articles/direct-boot to fix it.

AnatolyPristensky commented 2 years ago

@vvb2060 I've filed a bug on our internal dashboard. Thank you for the update.

AnastasiaKubova commented 2 years ago

Hi, @vvb2060! The App Center SDK doesn't support this behavior. Could you please try to check this workaround in your project before App Center start? Do it resolve your issue?

    private static String TAG = "AppCenterTag";
    private static String APP_CENTER_PREF_NAME = "AppCenter";
    private static String APP_CENTER_DATABASE = "com.microsoft.appcenter.persistence";

    private void supportDirectBootModeWorkaround(Context context) {
        Context deviceContext = context;

        // Get encryption status.
        DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
        int encryptionStatus = devicePolicyManager.getStorageEncryptionStatus();
        Log.d(TAG, "Current encryption status: " + encryptionStatus);

        // Move data if needed.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
                && (encryptionStatus == ENCRYPTION_STATUS_ACTIVE || encryptionStatus == ENCRYPTION_STATUS_ACTIVE_PER_USER)) {
            deviceContext = context.createDeviceProtectedStorageContext();
            if (!deviceContext.moveSharedPreferencesFrom(context,
                    APP_CENTER_PREF_NAME)) {
                Log.d(TAG, "Failed to migrate App Center shared preferences.");
            }
            if (!deviceContext.moveDatabaseFrom(context, APP_CENTER_DATABASE)) {
                Log.d(TAG, "Failed to migrate App Center database.");
            }
        }
    }
vvb2060 commented 2 years ago

I think this workaround won't fix the problem, you need to replace the context with deviceProtectedContext in appcenter library to get sp or db. I can't help you because I can only pass in the application object to initialize, not context.

AnastasiaKubova commented 2 years ago

Ok, I got it. Thanks for the quick check. The App Center SDK does not support this behavior for now. I will label this issue as a feature request. Also, feel free to open contribution PRs if you already have a solution.

canyie commented 2 years ago

Why this is a feature request? Both direct boot and FBE are introduced in Android 7.0, shouldn't it be a bug that the App Center SDK doesn't support features introduced in official Android nearly 6 years ago?

qqlittleice233 commented 2 years ago

When the device has been powered on but the user has not unlocked the device in Direct Boot Mode, components can't access the device encrypted storage and credential encrypted storage. To access device encrypted storage, you should use context.createDeviceProtectedStorageContext() instead of context. So it is a bug, isn't it?

AnastasiaKubova commented 2 years ago

The App Center SDK does not support work in this mode for now. We will start this feature as soon as possible. Contribution PRs are welcome.

vvb2060 commented 2 years ago

My workaround

    private static Application getDeApplication(Application app) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return app;
        var deApp = new Application() {
            @Override
            public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
                app.registerActivityLifecycleCallbacks(callback);
            }
        };
        try {
            var attach = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
            attach.setAccessible(true);
            attach.invoke(deApp, app.getApplicationContext().createDeviceProtectedStorageContext());
            return deApp;
        } catch (ReflectiveOperationException ignored) {
            return app;
        }
    }

    AppCenter.start(getDeApplication(app), "", Crashes.class);
DmitriyKirakosyan commented 2 years ago

The fix has been released! I'm closing the issue, but feel free to reopen if you face it again.