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
This commit is contained in:
Sunny Goyal
2024-09-27 13:56:45 -07:00
parent 644aad280b
commit 95045ce03a
4 changed files with 52 additions and 39 deletions
@@ -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) } }
}
@@ -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);
}
}
}
@@ -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;
}
}
@@ -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);
}
/**