Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 95045ce0 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Fixing test flakiness in Launcher initialization

Using same key for blob store and content provider based layouts

Using a single key for layout provider. This ensures that if one
test doesn't cleanup properly, any followup test which relies on
model initialization overrides the key appropriately

Bug: 370080120
Test: Presubmit
Flag: EXEMPT bugfix
Change-Id: Idef0a89a5d1ec89e24aae8a4549fd122634dad2f
parent 644aad28
Loading
Loading
Loading
Loading
+17 −16
Original line number Diff line number Diff line
@@ -35,9 +35,6 @@ import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
import android.provider.Settings.Secure
import android.text.Html
import android.util.AttributeSet
import android.util.Base64
import android.util.Base64.NO_PADDING
import android.util.Base64.NO_WRAP
import android.view.inputmethod.EditorInfo
import android.widget.TextView
import android.widget.Toast
@@ -57,9 +54,10 @@ import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY
import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL
import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG
import com.android.launcher3.LauncherSettings.Settings.LAYOUT_PROVIDER_KEY
import com.android.launcher3.LauncherSettings.Settings.createBlobProviderKey
import com.android.launcher3.R
import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.ItemInfo
@@ -241,7 +239,7 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
    private fun DebugInfo<Boolean>.getBoolValue() =
        DeviceConfigHelper.prefs.getBoolean(
            this.key,
            DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, this.key, this.valueInCode)
            DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, this.key, this.valueInCode),
        )

    private fun DebugInfo<Int>.getIntValueAsString() =
@@ -265,7 +263,7 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
        val pluginPermissionApps =
            pm.getPackagesHoldingPermissions(
                    arrayOf(PLUGIN_PERMISSION),
                    PackageManager.MATCH_DISABLED_COMPONENTS
                    PackageManager.MATCH_DISABLED_COMPONENTS,
                )
                .map { it.packageName }

@@ -274,7 +272,7 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
                pm.queryIntentServices(
                        Intent(action),
                        PackageManager.MATCH_DISABLED_COMPONENTS or
                            PackageManager.GET_RESOLVED_FILTER
                            PackageManager.GET_RESOLVED_FILTER,
                    )
                    .filter { pluginPermissionApps.contains(it.serviceInfo.packageName) }
            }
@@ -316,7 +314,7 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
                            infoList.forEach {
                                manager.pluginEnabler.setDisabled(
                                    it.serviceInfo.componentName,
                                    disabledState
                                    disabledState,
                                )
                            }
                            manager.notifyChange(Intent(Intent.ACTION_PACKAGE_CHANGED, pluginUri))
@@ -387,12 +385,12 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
            addOnboardPref(
                "All Apps Bounce",
                HOME_BOUNCE_SEEN.sharedPrefKey,
                HOME_BOUNCE_COUNT.sharedPrefKey
                HOME_BOUNCE_COUNT.sharedPrefKey,
            )
            addOnboardPref(
                "Hybrid Hotseat Education",
                HOTSEAT_DISCOVERY_TIP_COUNT.sharedPrefKey,
                HOTSEAT_LONGPRESS_TIP_SEEN.sharedPrefKey
                HOTSEAT_LONGPRESS_TIP_SEEN.sharedPrefKey,
            )
            addOnboardPref("Taskbar Education", TASKBAR_EDU_TOOLTIP_STEP.sharedPrefKey)
            addOnboardPref("Taskbar Search Education", TASKBAR_SEARCH_EDU_SEEN.sharedPrefKey)
@@ -470,13 +468,16 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
                        session.allowPublicAccess()

                        session.commit(ORDERED_BG_EXECUTOR) {
                            val key = Base64.encodeToString(digest, NO_WRAP or NO_PADDING)
                            Secure.putString(resolver, LAYOUT_DIGEST_KEY, key)
                            Secure.putString(
                                resolver,
                                LAYOUT_PROVIDER_KEY,
                                createBlobProviderKey(digest),
                            )

                            MODEL_EXECUTOR.submit { model.modelDbController.createEmptyDB() }.get()
                            MAIN_EXECUTOR.submit { model.forceReload() }.get()
                            MODEL_EXECUTOR.submit {}.get()
                            Secure.putString(resolver, LAYOUT_DIGEST_KEY, null)
                            Secure.putString(resolver, LAYOUT_PROVIDER_KEY, null)
                        }
                    }
                }
@@ -512,7 +513,7 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
                    info.providerName.className,
                    info.spanX,
                    info.spanY,
                    userType
                    userType,
                )
        }
    }
@@ -520,7 +521,7 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
    private fun createUriPickerIntent(
        action: String,
        executor: Executor,
        callback: (uri: Uri) -> Unit
        callback: (uri: Uri) -> Unit,
    ): Intent {
        val pendingIntent =
            PendingIntent(
@@ -532,7 +533,7 @@ class DevOptionsUiHelper(c: Context, attr: AttributeSet?) : PreferenceGroup(c, a
                        allowlistToken: IBinder?,
                        finishedReceiver: IIntentReceiver?,
                        requiredPermission: String?,
                        options: Bundle?
                        options: Bundle?,
                    ) {
                        intent.data?.let { uri -> executor.execute { callback(uri) } }
                    }
+14 −1
Original line number Diff line number Diff line
@@ -16,8 +16,12 @@

package com.android.launcher3;

import static android.util.Base64.NO_PADDING;
import static android.util.Base64.NO_WRAP;

import android.database.sqlite.SQLiteDatabase;
import android.provider.BaseColumns;
import android.util.Base64;

import androidx.annotation.NonNull;

@@ -354,8 +358,17 @@ public class LauncherSettings {
     * Launcher settings
     */
    public static final class Settings {
        public static final String LAYOUT_DIGEST_KEY = "launcher3.layout.provider.blob";
        public static final String LAYOUT_PROVIDER_KEY = "launcher3.layout.provider";
        public static final String LAYOUT_DIGEST_LABEL = "launcher-layout";
        public static final String LAYOUT_DIGEST_TAG = "ignore";
        public static final String BLOB_KEY_PREFIX = "blob://";

        /**
         * Creates a key to be used for {@link #LAYOUT_PROVIDER_KEY}
         * @param digest byte[] representing the message digest for the blob handle
         */
        public static String createBlobProviderKey(byte[] digest) {
            return BLOB_KEY_PREFIX + Base64.encodeToString(digest, NO_WRAP | NO_PADDING);
        }
    }
}
+16 −13
Original line number Diff line number Diff line
@@ -25,7 +25,8 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
import static com.android.launcher3.LauncherSettings.Settings.BLOB_KEY_PREFIX;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_PROVIDER_KEY;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
@@ -548,9 +549,15 @@ public class ModelDbController {
    private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(
            LauncherWidgetHolder widgetHolder) {
        ContentResolver cr = mContext.getContentResolver();
        String blobHandlerDigest = Settings.Secure.getString(cr, LAYOUT_DIGEST_KEY);
        if (!TextUtils.isEmpty(blobHandlerDigest)) {
        String systemLayoutProvider = Settings.Secure.getString(cr, LAYOUT_PROVIDER_KEY);
        if (TextUtils.isEmpty(systemLayoutProvider)) {
            return null;
        }

        // Try the blob store first
        if (systemLayoutProvider.startsWith(BLOB_KEY_PREFIX)) {
            BlobStoreManager blobManager = mContext.getSystemService(BlobStoreManager.class);
            String blobHandlerDigest = systemLayoutProvider.substring(BLOB_KEY_PREFIX.length());
            try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
                    blobManager.openBlob(BlobHandle.createWithSha256(
                            Base64.decode(blobHandlerDigest, NO_WRAP | NO_PADDING),
@@ -562,25 +569,21 @@ public class ModelDbController {
            }
        }

        String authority = Settings.Secure.getString(cr, "launcher3.layout.provider");
        if (TextUtils.isEmpty(authority)) {
            return null;
        }

        // Try contentProvider based provider
        PackageManager pm = mContext.getPackageManager();
        ProviderInfo pi = pm.resolveContentProvider(authority, 0);
        ProviderInfo pi = pm.resolveContentProvider(systemLayoutProvider, 0);
        if (pi == null) {
            Log.e(TAG, "No provider found for authority " + authority);
            Log.e(TAG, "No provider found for authority " + systemLayoutProvider);
            return null;
        }
        Uri uri = getLayoutUri(authority, mContext);
        Uri uri = getLayoutUri(systemLayoutProvider, mContext);
        try (InputStream in = cr.openInputStream(uri)) {
            Log.d(TAG, "Loading layout from " + authority);
            Log.d(TAG, "Loading layout from " + systemLayoutProvider);

            Resources res = pm.getResourcesForApplication(pi.applicationInfo);
            return getAutoInstallsLayoutFromIS(in, widgetHolder, SourceResources.wrap(res));
        } catch (Exception e) {
            Log.e(TAG, "Error getting layout stream from: " + authority , e);
            Log.e(TAG, "Error getting layout stream from: " + systemLayoutProvider , e);
            return null;
        }
    }
+5 −9
Original line number Diff line number Diff line
@@ -15,14 +15,12 @@
 */
package com.android.launcher3.util;

import static android.util.Base64.NO_PADDING;
import static android.util.Base64.NO_WRAP;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_PROVIDER_KEY;
import static com.android.launcher3.LauncherSettings.Settings.createBlobProviderKey;

import static org.junit.Assert.assertTrue;

@@ -42,7 +40,6 @@ import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.system.OsConstants;
import android.util.Base64;
import android.util.Log;

import androidx.test.uiautomator.UiDevice;
@@ -169,13 +166,12 @@ public class TestUtil {
            session.commit(AsyncTask.THREAD_POOL_EXECUTOR, i -> wait.countDown());
        }

        String key = Base64.encodeToString(digest, NO_WRAP | NO_PADDING);

        grantWriteSecurePermission();
        Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, key);
        Settings.Secure.putString(
                context.getContentResolver(), LAYOUT_PROVIDER_KEY, createBlobProviderKey(digest));
        wait.await();
        return () ->
            Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null);
            Settings.Secure.putString(context.getContentResolver(), LAYOUT_PROVIDER_KEY, null);
    }

    /**