Merge cros/upstream to cros/main - 1.23.4-dev

Part of an uprev that contains the following commits:

" 6a8797927 build: unstable release version bump tp 1.23.4 (Aleksander Morgado)"
" 4d360e3b2 plugin: include MM_PLUGIN_ALLOWED_SINGLE_AT in port probing (Garfield Watkins)"
" 004450f9a base-modem: retry AT+CNUM a couple times if the SIM is busy (Dan Williams)"
" ded76a0ab base-modem-at: add optional wait before sending command in a sequence (Dan Williams)"
" b3708636b mtk: fix reported nw error in FM350 < 29.22.13 (Aleksander Morgado)"
" 8dffb76f1 broadband-modem-mbim: allow normalizing the nw error value (Aleksander Morgado)"
" b313a603a mtk: remove IP packet filters during connection in FM350 < 29.23.06 (Aleksander Morgado)"
" 2f306e8e7 mtk: multiplex support only available in FM350 >= 29.23.06 (Aleksander Morgado)"
" 2f5ebfade iface-modem: create bearer list after loading revision (Aleksander Morgado)"
" 1373ca7e5 mtk: expect async SLAAC only in FM350 >= 29.23.06 (Aleksander Morgado)"
" f31f42dda bearer-mbim: support async IP configuration indications reporting SLAAC result (Aleksander Morgado)"
" b0add8c03 broadband-modem-mbim: request IP configuration indications (Aleksander Morgado)"
" 1befca457 cinterion: disable periodic signal checks in MBIM modems (Aleksander Morgado)"
" 91828ecc6 quectel: disable periodic signal checks in MBIM modems (Aleksander Morgado)"
" 3c35212f7 telit: disable periodic signal checks in MBIM modems (Aleksander Morgado)"
" 29d237bcb .gitignore: ignore Visual Studio Code directory and file (Yegor Yefremov)"
" ef126455b plugins: mtk: Added MT6229 USB modem support. (RICCIARDI-Adrien)"
" b61adb162 iface-modem: also run disable on failed low power mode update (Aleksander Morgado)"
" f6795754b broadband-modem-qmi: change operator description priority (Florian Eckert)"
" 521349d5f broadband-modem-qmi: add debug messages on operator description set (Florian Eckert)"
" 778d953f5 broadband-modem-mbim: use MBIMEx to list profiles unconditionally if available (Aleksander Morgado)"
" c80e92e4a broadband-modem-mbim: don't ignore profiles with "none" context type (Aleksander Morgado)"
" 2ac4bda84 libmm-glib,3gpp-profile: fix loading roaming allowance in DBus dictionary (Aleksander Morgado)"
" 06eafc8c1 libmm-glib,3gpp-profile: fix loading profile source in DBus dictionary (Aleksander Morgado)"
" 97ce69fe9 broadband-modem-mbim: fix processing IP type from MbimProvisionedContextElementV2 (Aleksander Morgado)"
" a729a92e1 build: fix fs.copyfile usage for meson versions 0.64/0.64.1 (Yegor Yefremov)"

BUG=b:325923333
FIXED=b:325923333

TEST=CQ passes

Change-Id: I4a5deab14b9aac08c63b34f82c29733aa26e32a1
diff --git a/DIR_METADATA b/DIR_METADATA
new file mode 100644
index 0000000..5d046e9
--- /dev/null
+++ b/DIR_METADATA
@@ -0,0 +1,43 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+team_email: "cros-cellular-core@google.com"
+
+buganizer {
+  # https://b.corp.google.com/issues?q=status:open%20componentid:167157
+  # ChromeOS > Platform > Connectivity > Cellular
+  component_id: 167157
+}
+
+chromeos {
+  cq {
+    # See go/cros-cq-test-config
+    source_test_plans {
+      test_plan_starlark_files {
+        host: "chrome-internal.googlesource.com"
+        project: "chromeos/config-internal"
+        path: "test/plans/v2/ctpv1_compatible/legacy_default_tast_hw.star"
+      }
+      test_plan_starlark_files {
+        host: "chrome-internal.googlesource.com"
+        project: "chromeos/config-internal"
+        path: "test/plans/v2/ctpv1_compatible/legacy_default_autotest_hw.star"
+      }
+      test_plan_starlark_files {
+        host: "chrome-internal.googlesource.com"
+        project: "chromeos/config-internal"
+        path: "test/plans/v2/ctpv1_compatible/legacy_default_vm.star"
+      }
+      test_plan_starlark_files {
+        host: "chrome-internal.googlesource.com"
+        project: "chromeos/config-internal"
+        path: "test/plans/v2/ctpv1_compatible/cellular_cq.star"
+      }
+    }
+  }
+}
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..9712978
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+ejcaruso@chromium.org
+pholla@chromium.org
+andrewlassalle@chromium.org
+madhavadas@google.com
+nmarupaka@google.com
+aleksandermj@google.com
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
new file mode 100644
index 0000000..51d8dd6
--- /dev/null
+++ b/PRESUBMIT.cfg
@@ -0,0 +1,9 @@
+# This sample config file disables all of the ChromiumOS source style checks.
+# Comment out the disable-flags for any checks you want to leave enabled.
+
+[Hook Overrides]
+stray_whitespace_check: false
+long_line_check: false
+cros_license_check: false
+tab_check: false
+
diff --git a/README.chromium b/README.chromium
new file mode 100644
index 0000000..adfc720
--- /dev/null
+++ b/README.chromium
@@ -0,0 +1,19 @@
+DESCRIPTION="Broadband modem support daemon"
+HOMEPAGE="https://modemmanager.org"
+UPSTREAM_REPO="git://anongit.freedesktop.org/ModemManager/ModemManager"
+LOCAL_GIT_REPO="https://chromium.googlesource.com/chromiumos/third_party/modemmanager-next.git"
+UPSTREAM_BUGSDB="https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues"
+LOCAL_BUGSDB="http://buganizer.corp.google.com"
+LICENSE="GPLv2"
+LICENSE_FILE="COPYING"
+
+Description:
+
+ModemManager provides a DBus interface to control broadband modem
+devices. The intended user is a network manager program, such as
+NetworkManager, flimflam, or shill.
+
+This repository mirrors the main branch of the upstream repository.
+
+Local changes should be minimal, but support for particular modems may
+make it here before they make it upstream.
diff --git a/src/mm-base-bearer.c b/src/mm-base-bearer.c
index de2bd5f..a898971 100644
--- a/src/mm-base-bearer.c
+++ b/src/mm-base-bearer.c
@@ -862,13 +862,16 @@
                    GTask        *task)
 {
     MMBearerConnectResult *result;
+    const gchar           *data_interface;
 
     result = g_task_get_task_data (task);
 
+    data_interface = mm_port_get_device (mm_bearer_connect_result_peek_data (result));
+
     /* Update bearer and interface status */
     bearer_update_status_connected (
         self,
-        mm_port_get_device (mm_bearer_connect_result_peek_data (result)),
+        data_interface,
         mm_bearer_connect_result_get_multiplexed (result),
         mm_bearer_connect_result_get_profile_id (result),
         mm_bearer_connect_result_peek_ipv4_config (result),
diff --git a/src/mm-base-sim.c b/src/mm-base-sim.c
index d70bf56..ce009cc 100644
--- a/src/mm-base-sim.c
+++ b/src/mm-base-sim.c
@@ -2377,6 +2377,7 @@
     INITIALIZATION_STEP_SIM_IDENTIFIER,
     INITIALIZATION_STEP_IMSI,
     INITIALIZATION_STEP_OPERATOR_ID,
+    INITIALIZATION_STEP_OPERATOR_ID_RETRY,
     INITIALIZATION_STEP_OPERATOR_NAME,
     INITIALIZATION_STEP_EMERGENCY_NUMBERS,
     INITIALIZATION_STEP_PREFERRED_NETWORKS,
@@ -2761,6 +2762,26 @@
         ctx->step++;
         /* Fall through */
 
+    case INITIALIZATION_STEP_OPERATOR_ID_RETRY:
+        /* This is a retry of the previous step. The query only happens if the
+         *  operator_identifier is still null. */
+        /* Don't load operator ID if the SIM is known to be an eSIM without
+         * profiles; otherwise (if physical SIM, or if eSIM with profile, or if
+         * SIM type unknown) try to load it. */
+        if (IS_ESIM_WITHOUT_PROFILES (self))
+            mm_obj_dbg (self, "not loading operator ID in eSIM without profiles");
+        else if (mm_gdbus_sim_get_operator_identifier (MM_GDBUS_SIM (self)) == NULL &&
+                 MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier &&
+                 MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier_finish) {
+            MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier (
+                self,
+                (GAsyncReadyCallback)init_load_operator_identifier_ready,
+                task);
+            return;
+        }
+        ctx->step++;
+        /* Fall through */
+
     case INITIALIZATION_STEP_OPERATOR_NAME:
         /* Don't load operator name if the SIM is known to be an eSIM without
          * profiles; otherwise (if physical SIM, or if eSIM with profile, or if
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c
index 0f2f849..5d1ec03 100644
--- a/src/mm-broadband-modem-mbim.c
+++ b/src/mm-broadband-modem-mbim.c
@@ -399,6 +399,14 @@
     return MM_BROADBAND_MODEM_MBIM_GET_CLASS (self)->peek_port_mbim_for_data (self, data, error);
 }
 
+gboolean
+mm_broadband_modem_mbim_get_is_lte_attach_info_supported (MMBroadbandModemMbim  *self)
+{
+    g_assert (MM_IS_BROADBAND_MODEM_MBIM (self));
+
+    return self->priv->is_lte_attach_info_supported;
+}
+
 /*****************************************************************************/
 
 guint32
@@ -1721,7 +1729,8 @@
 
     /* Initialized */
     if (ready_state == MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED ||
-        ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED) {
+        ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED ||
+        ready_state == MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE) {
         MbimMessage *message;
 
         /* Query which lock is to unlock */
@@ -9099,7 +9108,11 @@
      * the backoff index set to the current index of modem
      */
     config_state = g_new (MbimSarConfigState, 1);
+#if defined MBIM_FIBOCOM_SAR_HACK //TODO(b/188002987): Remove hacks before merging to upstream
+    config_state->antenna_index = 0;
+#else
     config_state->antenna_index = 0xFFFFFFFF;
+#endif
     config_state->backoff_index = mm_iface_modem_sar_get_power_level (_self);
 
     g_task_set_task_data (task, GUINT_TO_POINTER (config_state->backoff_index), NULL);
@@ -9164,7 +9177,11 @@
      * the backoff index set to the input power level
      */
     config_state = g_new (MbimSarConfigState, 1);
+#if defined MBIM_FIBOCOM_SAR_HACK //TODO(b/188002987): Remove hacks before merging to upstream
+    config_state->antenna_index = 0;
+#else
     config_state->antenna_index = 0xFFFFFFFF;
+#endif
     config_state->backoff_index = power_level;
 
     task = g_task_new (self, NULL, callback, user_data);
diff --git a/src/mm-broadband-modem-mbim.h b/src/mm-broadband-modem-mbim.h
index 968a576..4e2c06c 100644
--- a/src/mm-broadband-modem-mbim.h
+++ b/src/mm-broadband-modem-mbim.h
@@ -66,6 +66,8 @@
                                                              MMPort                *data,
                                                              GError               **error);
 
+gboolean mm_broadband_modem_mbim_get_is_lte_attach_info_supported (MMBroadbandModemMbim  *self);
+
 guint32    mm_broadband_modem_mbim_normalize_nw_error       (MMBroadbandModemMbim *self,
                                                              guint32               nw_error);
 
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index b4a3726..f71231b 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -113,6 +113,13 @@
     /* Index of the WDS profile used as initial EPS bearer */
     guint16 default_attach_pdn;
 
+    /* The WDS profile for the initial EPS bearer is owned by ModemManager
+     * and can be modified */
+    gboolean mm_owned_attach_pdn;
+
+    /* The current PDN list in the modem */
+    GArray *current_pdn_list;
+
     /* Support for the APN type mask in profiles */
     gboolean apn_type_not_supported;
 
@@ -11457,20 +11464,26 @@
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN,
+    SET_INITIAL_EPS_BEARER_SETTINGS_STEP_HANDLE_APP_PROFILE,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE,
+    SET_INITIAL_EPS_BEARER_SETTINGS_STEP_SET_LTE_ATTACH_PDN,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP,
     SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LAST_SETTING,
 } SetInitialEpsBearerSettingsStep;
 
 typedef struct {
     SetInitialEpsBearerSettingsStep  step;
+    QmiClientWds                    *client;
     MM3gppProfile                   *profile;
     MMModemPowerState                power_state;
+    gboolean                         setting_mm_owned_pdn;
+    gboolean                         update_lte_attach_pdn;
 } SetInitialEpsBearerSettingsContext;
 
 static void
 set_initial_eps_bearer_settings_context_free (SetInitialEpsBearerSettingsContext *ctx)
 {
+    g_clear_object (&ctx->client);
     g_clear_object (&ctx->profile);
     g_slice_free (SetInitialEpsBearerSettingsContext, ctx);
 }
@@ -11507,6 +11520,67 @@
 }
 
 static void
+set_initial_eps_bearer_set_lte_attach_pdn_ready (QmiClientWds *client,
+                                                 GAsyncResult *res,
+                                                 GTask        *task)
+{
+    g_autoptr(QmiMessageWdsSetLteAttachPdnListOutput)  output = NULL;
+    GError                                            *error = NULL;
+    MMBroadbandModemQmi                               *self;
+    SetInitialEpsBearerSettingsContext                *ctx;
+
+    self = g_task_get_source_object (task);
+    ctx = g_task_get_task_data (task);
+
+    output = qmi_client_wds_set_lte_attach_pdn_list_finish (client, res, &error);
+    if (!output) {
+        g_prefix_error (&error, "QMI operation failed: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    if (!qmi_message_wds_set_lte_attach_pdn_list_output_get_result (output, &error)) {
+        g_prefix_error (&error, "Couldn't set the LTE attach PDN list: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    self->priv->mm_owned_attach_pdn = ctx->setting_mm_owned_pdn;
+
+    ctx->step++;
+    set_initial_eps_bearer_settings_step (task);
+}
+
+static void
+set_initial_eps_bearer_set_lte_attach_pdn (GTask *task)
+{
+    g_autoptr(QmiMessageWdsSetLteAttachPdnListInput)  input = NULL;
+    MMBroadbandModemQmi                *self;
+    SetInitialEpsBearerSettingsContext *ctx;
+
+    self = g_task_get_source_object (task);
+    ctx  = g_task_get_task_data (task);
+
+    input = qmi_message_wds_set_lte_attach_pdn_list_input_new ();
+    qmi_message_wds_set_lte_attach_pdn_list_input_set_list (input,
+                                                            self->priv->current_pdn_list,
+                                                            NULL);
+
+    qmi_message_wds_set_lte_attach_pdn_list_input_set_action (input,
+                                                              QMI_WDS_ATTACH_PDN_LIST_ACTION_DETACH_OR_PDN_DISCONNECT,
+                                                              NULL);
+
+    qmi_client_wds_set_lte_attach_pdn_list (ctx->client,
+                                            input,
+                                            10,
+                                            NULL,
+                                            (GAsyncReadyCallback)set_initial_eps_bearer_set_lte_attach_pdn_ready,
+                                            task);
+}
+
+static void
 set_initial_eps_bearer_modify_profile_ready (MMIfaceModem3gppProfileManager *self,
                                              GAsyncResult                   *res,
                                              GTask                          *task)
@@ -11524,6 +11598,13 @@
         return;
     }
 
+    if (mm_3gpp_profile_get_profile_id(ctx->profile) == MM_3GPP_PROFILE_ID_UNKNOWN) {
+        // The profile was just created.
+        ctx->update_lte_attach_pdn = TRUE;
+        /* Update |default_attach_pdn| now so it can be modified in the next step. */
+        MM_BROADBAND_MODEM_QMI(self)->priv->default_attach_pdn = (guint16) mm_3gpp_profile_get_profile_id (stored);
+        g_array_prepend_val (MM_BROADBAND_MODEM_QMI(self)->priv->current_pdn_list, MM_BROADBAND_MODEM_QMI(self)->priv->default_attach_pdn);
+    }
     ctx->step++;
     set_initial_eps_bearer_settings_step (task);
 }
@@ -11546,6 +11627,47 @@
 }
 
 static void
+set_initial_eps_bearer_delete_mm_profile_ready (MMIfaceModem3gppProfileManager *self,
+                                                GAsyncResult                   *res,
+                                                GTask                          *task)
+{
+    GError                             *error = NULL;
+    SetInitialEpsBearerSettingsContext *ctx;
+
+    ctx = g_task_get_task_data (task);
+
+    if (!modem_3gpp_profile_manager_delete_profile_finish (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER(self), res, &error)) {
+        g_prefix_error (&error, "Couldn't delete the profile: ");
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_SET_LTE_ATTACH_PDN;
+    ctx->update_lte_attach_pdn = TRUE;
+    MM_BROADBAND_MODEM_QMI(self)->priv->current_pdn_list = g_array_remove_index (MM_BROADBAND_MODEM_QMI(self)->priv->current_pdn_list, 0);
+    if (MM_BROADBAND_MODEM_QMI(self)->priv->current_pdn_list->len > 0)
+        MM_BROADBAND_MODEM_QMI(self)->priv->default_attach_pdn = g_array_index (MM_BROADBAND_MODEM_QMI(self)->priv->current_pdn_list, guint16, 0);
+    set_initial_eps_bearer_settings_step (task);
+}
+
+static void
+set_initial_eps_bearer_delete_mm_profile (GTask *task)
+{
+    MMBroadbandModemQmi                *self;
+    SetInitialEpsBearerSettingsContext *ctx;
+
+    self = g_task_get_source_object (task);
+    ctx  = g_task_get_task_data (task);
+
+    modem_3gpp_profile_manager_delete_profile (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+                                               ctx->profile,
+                                               "profile-id",
+                                               (GAsyncReadyCallback)set_initial_eps_bearer_delete_mm_profile_ready,
+                                               task);
+}
+
+static void
 set_initial_eps_bearer_power_down_ready (MMIfaceModem *self,
                                          GAsyncResult *res,
                                          GTask        *task)
@@ -11593,6 +11715,7 @@
 {
     SetInitialEpsBearerSettingsContext *ctx;
     MMBroadbandModemQmi                *self;
+    const gchar*                        apn_name;
 
     self = g_task_get_source_object (task);
     ctx  = g_task_get_task_data (task);
@@ -11620,11 +11743,55 @@
             ctx->step++;
             /* fall through */
 
+        case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_HANDLE_APP_PROFILE:
+            apn_name = mm_3gpp_profile_get_apn(ctx->profile);
+            ctx->update_lte_attach_pdn = FALSE;
+            mm_obj_info (self, "Set Initial eps settings: apn_name: %s", apn_name);
+            if (apn_name && g_strcmp0 (apn_name, "") != 0) {
+                ctx->setting_mm_owned_pdn = TRUE;
+                mm_3gpp_profile_set_profile_name(ctx->profile, MM_BROADBAND_MODEM_QMI_PROFILE_NAME);
+                if (self->priv->mm_owned_attach_pdn) {
+                    mm_obj_info (self, "Overriding MM owned profile %d with APN: %s.",
+                        self->priv->default_attach_pdn, apn_name);
+                    ctx->step++;
+                    /* fall through */
+                } else {
+                    mm_obj_info (self, "creating a profile for initial EPS bearer settings...");
+                    mm_3gpp_profile_set_profile_id(ctx->profile, MM_3GPP_PROFILE_ID_UNKNOWN);
+                    ctx->update_lte_attach_pdn = FALSE;
+                    ctx->step++;
+                    /* fall through */
+                }
+            } else {
+                /* Note: Never store an empty APN on the mm_owned_profile_index, since the logic in iface-modem-3gpp
+                   will skip the reattach if the new settings are also empty, and we might get stuck with empty APN
+                   settings which have the wrong |ip_type|. */
+                mm_obj_info (self, "Empty APN name provided. Falling back to modem profile for Attach APN.");
+                ctx->setting_mm_owned_pdn = FALSE;
+                if (self->priv->mm_owned_attach_pdn) {
+                    mm_obj_info (self, "Deleting MM owned profile for initial EPS bearer settings...");
+                    set_initial_eps_bearer_delete_mm_profile (task);
+                } else {
+                    ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP;
+                    set_initial_eps_bearer_settings_step (task);
+                }
+                return;
+            }
+
         case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE:
             mm_obj_dbg (self, "modifying initial EPS bearer settings profile...");
             set_initial_eps_bearer_modify_profile (task);
             return;
 
+        case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_SET_LTE_ATTACH_PDN:
+            if (ctx->update_lte_attach_pdn) {
+                mm_obj_info (self, "updating lte attach pdn after changing initial EPS bearer settings...");
+                set_initial_eps_bearer_set_lte_attach_pdn (task);
+                return;
+            }
+            ctx->step++;
+            /* fall through */
+
         case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP:
             if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
                 mm_obj_dbg (self, "powering up after changing initial EPS bearer settings...");
@@ -11655,6 +11822,13 @@
     SetInitialEpsBearerSettingsContext *ctx;
     GTask                              *task;
     MM3gppProfile                      *profile;
+    QmiClient                          *client;
+
+
+    if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+                                      QMI_SERVICE_WDS, &client,
+                                      callback, user_data))
+        return;
 
     task = g_task_new (self, NULL, callback, user_data);
 
@@ -11670,6 +11844,7 @@
 
     ctx = g_slice_new0 (SetInitialEpsBearerSettingsContext);
     ctx->profile = g_object_ref (profile);
+    ctx->client = QMI_CLIENT_WDS (g_object_ref (client));
     ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST;
     g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_bearer_settings_context_free);
 
@@ -11695,6 +11870,9 @@
     GError                   *error = NULL;
     g_autoptr(MM3gppProfile)  profile = NULL;
     MMBearerProperties       *properties;
+    MMBroadbandModemQmi      *self;
+
+    self = g_task_get_source_object (task);
 
     profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (_self, res, &error);
     if (!profile) {
@@ -11703,6 +11881,10 @@
         return;
     }
 
+    self->priv->mm_owned_attach_pdn = FALSE;
+    if (g_strcmp0(mm_3gpp_profile_get_profile_name (profile), MM_BROADBAND_MODEM_QMI_PROFILE_NAME) == 0)
+            self->priv->mm_owned_attach_pdn = TRUE;
+
     properties = mm_bearer_properties_new_from_profile (profile, &error);
     if (!properties)
         g_task_return_error (task, error);
@@ -11761,8 +11943,12 @@
         return;
     }
 
+    /* Resize array to match |current_list| */
+    g_array_set_size (self->priv->current_pdn_list, current_list->len);
+
     mm_obj_dbg (self, "Found %u LTE attach PDNs defined", current_list->len);
     for (i = 0; i < current_list->len; i++) {
+        g_array_index (self->priv->current_pdn_list, guint16, i) = g_array_index (current_list, guint16, i);
         if (i == 0) {
             self->priv->default_attach_pdn = g_array_index (current_list, guint16, i);
             mm_obj_dbg (self, "Default LTE attach PDN profile: %u", self->priv->default_attach_pdn);
@@ -13692,6 +13878,7 @@
     self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
                                               MM_TYPE_BROADBAND_MODEM_QMI,
                                               MMBroadbandModemQmiPrivate);
+    self->priv->current_pdn_list = g_array_new (FALSE, FALSE, sizeof (guint16));
 }
 
 static void
@@ -13707,6 +13894,9 @@
     if (self->priv->supported_bands)
         g_array_unref (self->priv->supported_bands);
 
+    if (self->priv->current_pdn_list)
+        g_array_free (self->priv->current_pdn_list, TRUE);
+
     G_OBJECT_CLASS (mm_broadband_modem_qmi_parent_class)->finalize (object);
 }
 
diff --git a/src/mm-broadband-modem-qmi.h b/src/mm-broadband-modem-qmi.h
index 97a1a77..d5b4ac7 100644
--- a/src/mm-broadband-modem-qmi.h
+++ b/src/mm-broadband-modem-qmi.h
@@ -25,6 +25,9 @@
 #define MM_IS_BROADBAND_MODEM_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MM_TYPE_BROADBAND_MODEM_QMI))
 #define MM_BROADBAND_MODEM_QMI_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MM_TYPE_BROADBAND_MODEM_QMI, MMBroadbandModemQmiClass))
 
+/* Chromium OS specific profile */
+#define MM_BROADBAND_MODEM_QMI_PROFILE_NAME    "CrOS_attach_PDN"
+
 typedef struct _MMBroadbandModemQmi MMBroadbandModemQmi;
 typedef struct _MMBroadbandModemQmiClass MMBroadbandModemQmiClass;
 typedef struct _MMBroadbandModemQmiPrivate MMBroadbandModemQmiPrivate;
diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c
index a742693..c2908de 100644
--- a/src/mm-iface-modem-3gpp.c
+++ b/src/mm-iface-modem-3gpp.c
@@ -1243,7 +1243,11 @@
         return;
     }
 
-    if (!mm_bearer_properties_cmp (new_config, ctx->config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) {
+    /* When we request to set an empty/NULL APN, we instead select the default initial attach APN used by the modem,
+       which will probably not be an empty APN. If that's the case, we won't check for equality between the requested
+       APN and the APN which is now set. */
+    if (mm_bearer_properties_get_apn (ctx->config) && g_strcmp0 (mm_bearer_properties_get_apn (ctx->config), "") != 0 &&
+        !mm_bearer_properties_cmp (new_config, ctx->config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) {
         mm_obj_warn (self, "requested and reloaded initial EPS bearer settings don't match");
         mm_obj_info (self, "reloaded initial EPS bearer settings:");
         mm_log_bearer_properties (self, MM_LOG_LEVEL_INFO, "  ", new_config);
diff --git a/src/plugins/fibocom/77-mm-fibocom-port-types.rules b/src/plugins/fibocom/77-mm-fibocom-port-types.rules
index d4fd239..ead2a80 100644
--- a/src/plugins/fibocom/77-mm-fibocom-port-types.rules
+++ b/src/plugins/fibocom/77-mm-fibocom-port-types.rules
@@ -11,12 +11,22 @@
 # Fibocom L850-GL attach APN with toggle modem power
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{ID_MM_FIBOCOM_INITIAL_EPS_OFF_ON}="1"
 
+# Fibocom FM101-GL attach APN with toggle modem power
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{ID_MM_FIBOCOM_INITIAL_EPS_OFF_ON}="1"
+
 # Fibocom L850-GL
 #  ttyACM0 (if #2): AT port
 #  ttyACM1 (if #4): debug port (ignore)
 #  ttyACM2 (if #6): AT port
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
 
+# Fibocom L850-GL: ChromeOS specific configuration
+#  cdc-wdm0 (if #0): flagged as required control port
+#  ttyACM0 (if #2): managed by modemfwd (ignored in MM)
+#  ttyACM2 (if #6): managed by ModemManager
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_REQUIRED}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
+
 # Fibocom NL668-AM
 #  ttyACM0 (if #2): AT port
 #  ttyACM1 (if #3): AT port
@@ -25,6 +35,12 @@
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
 
+# Fibocom NL668-AM: ChromeOS specific configuration
+#  cdc-wdm0 (if #0): flagged as required control port
+#  ttyACM0 (if #2): managed by modemfwd (ignored in MM)
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_REQUIRED}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_IGNORE}="1"
+
 # Fibocom NL668 doesn't support multiplexing properly
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{ID_MM_MAX_MULTIPLEXED_LINKS}="0"
 
@@ -49,6 +65,12 @@
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1"
 ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_FIBOCOM_FASTBOOT}="1"
 
+# Fibocom FM101-GL (MBIM): ChromeOS specific configuration
+#  cdc-wdm0 (if #0): flagged as required control port
+#  ttyUSB0 (if #2): ignored in MM
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_REQUIRED}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a2", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_IGNORE}="1"
+
 # Fibocom FM101-GL (ADB)
 #  ttyUSB0 (if #2): debug port (ignore)
 #  ttyUSB1 (if #3): AT port
diff --git a/src/plugins/fibocom/mm-shared-fibocom.c b/src/plugins/fibocom/mm-shared-fibocom.c
index 30416df..c4d5ef8 100644
--- a/src/plugins/fibocom/mm-shared-fibocom.c
+++ b/src/plugins/fibocom/mm-shared-fibocom.c
@@ -27,6 +27,8 @@
 #include "mm-broadband-modem-mbim.h"
 #include "mm-iface-modem.h"
 #include "mm-iface-modem-3gpp.h"
+#include "mm-modem-helpers-mbim.h"
+#include "mm-port-mbim.h"
 #include "mm-shared-fibocom.h"
 #include "mm-base-modem-at.h"
 
@@ -126,6 +128,209 @@
     set_initial_eps_bearer_settings_complete (task);
 }
 
+
+
+static gboolean
+peek_device (gpointer              self,
+             MbimDevice          **o_device,
+             GAsyncReadyCallback   callback,
+             gpointer              user_data)
+{
+    MMPortMbim *port;
+
+    port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
+    if (!port) {
+        g_task_report_new_error (self, callback, user_data, peek_device,
+                                 MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't peek MBIM port");
+        return FALSE;
+    }
+
+    *o_device = mm_port_mbim_peek_device (port);
+    return TRUE;
+}
+
+
+static void
+parent_att_hack_set_lte_attach_configuration_set_ready (MbimDevice   *device,
+                                                        GAsyncResult *res,
+                                                        GTask        *task)
+{
+    MbimMessage          *response;
+    GError               *error = NULL;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error))
+        g_task_return_error (task, error);
+    else
+        g_task_return_boolean (task, TRUE);
+    g_object_unref (task);
+
+    if (response)
+        mbim_message_unref (response);
+}
+
+/* This function is almost identical to before_set_lte_attach_configuration_query_ready in mm-broadband-modem-mbim.c
+ * The only difference is the code related to ptr_home/ptr_partner/ptr_non_partner and the flow that is executed after this function. */
+static void
+parent_att_hack_before_set_lte_attach_configuration_query_ready (MbimDevice   *device,
+                                                                 GAsyncResult *res,
+                                                                 GTask        *task)
+{
+    MMBroadbandModemMbim                       *self;
+    MbimMessage                                *request;
+    MbimMessage                                *response;
+    GError                                     *error = NULL;
+    MMBearerProperties                         *config;
+    guint32                                     n_configurations = 0;
+    MbimLteAttachConfiguration                **configurations = NULL;
+    gint                                        i;
+    MbimLteAttachConfiguration *ptr_home = NULL, *ptr_partner = NULL, *ptr_non_partner = NULL;
+
+    self   = g_task_get_source_object (task);
+    config = g_task_get_task_data (task);
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response ||
+        !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+        !mbim_message_ms_basic_connect_extensions_lte_attach_configuration_response_parse (
+            response,
+            &n_configurations,
+            &configurations,
+            &error)) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        goto out;
+    }
+
+    /* We should always receive 3 configurations but the MBIM API doesn't force
+     * that so we'll just assume we don't get always the same fixed number */
+    for (i = 0; i < n_configurations; i++) {
+        MMBearerIpFamily ip_family;
+        MMBearerAllowedAuth auth;
+
+        /* We only support configuring the HOME settings */
+        if (configurations[i]->roaming != MBIM_LTE_ATTACH_CONTEXT_ROAMING_CONTROL_HOME)
+            continue;
+
+        ip_family = mm_bearer_properties_get_ip_type (config);
+        if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY)
+            configurations[i]->ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
+        else {
+            configurations[i]->ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_family, &error);
+            if (error) {
+                configurations[i]->ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
+                mm_obj_warn (self, "unexpected IP type settings requested: %s", error->message);
+                g_clear_error (&error);
+            }
+        }
+
+        g_clear_pointer (&(configurations[i]->access_string), g_free);
+        configurations[i]->access_string = g_strdup (mm_bearer_properties_get_apn (config));
+
+        g_clear_pointer (&(configurations[i]->user_name), g_free);
+        configurations[i]->user_name = g_strdup (mm_bearer_properties_get_user (config));
+
+        g_clear_pointer (&(configurations[i]->password), g_free);
+        configurations[i]->password = g_strdup (mm_bearer_properties_get_password (config));
+
+        auth = mm_bearer_properties_get_allowed_auth (config);
+        if ((auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) || configurations[i]->user_name || configurations[i]->password) {
+            configurations[i]->auth_protocol = mm_bearer_allowed_auth_to_mbim_auth_protocol (auth, self, &error);
+            if (error) {
+                configurations[i]->auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
+                mm_obj_warn (self, "unexpected auth settings requested: %s", error->message);
+                g_clear_error (&error);
+            }
+        } else {
+            configurations[i]->auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
+        }
+
+        configurations[i]->source = MBIM_CONTEXT_SOURCE_USER;
+        configurations[i]->compression = MBIM_COMPRESSION_NONE;
+        break;
+    }
+
+    /* Code customization start b/224986971 */
+    for (i = 0; i < n_configurations; i++) {
+        if (configurations[i]->roaming == MBIM_LTE_ATTACH_CONTEXT_ROAMING_CONTROL_HOME)
+            ptr_home = configurations[i];
+        else if (configurations[i]->roaming == MBIM_LTE_ATTACH_CONTEXT_ROAMING_CONTROL_NON_PARTNER)
+            ptr_non_partner = configurations[i];
+        else
+            ptr_partner = configurations[i];
+    }
+    /* Sort the profiles in the order home, non_partner and partner, so we can easily remove the last one(partner). */
+    if (n_configurations == 3 && ptr_home && ptr_partner && ptr_non_partner) {
+        mm_obj_info (self, "removing partner profile");
+        configurations[0] = ptr_home;
+        configurations[1] = ptr_non_partner;
+        configurations[2] = ptr_partner;
+        n_configurations = 2;
+    }
+    /* Code customization end b/224986971 */
+
+    request = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_set_new (
+                  MBIM_LTE_ATTACH_CONTEXT_OPERATION_DEFAULT,
+                  n_configurations,
+                  (const MbimLteAttachConfiguration *const *)configurations,
+                  &error);
+    if (!request) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        goto out;
+    }
+    mbim_device_command (device,
+                         request,
+                         10,
+                         NULL,
+                         (GAsyncReadyCallback)parent_att_hack_set_lte_attach_configuration_set_ready,
+                         task);
+    mbim_message_unref (request);
+
+ out:
+    if (configurations)
+        mbim_lte_attach_configuration_array_free (configurations);
+
+    if (response)
+        mbim_message_unref (response);
+}
+
+/* This function is functionally identical to set_initial_eps_bearer_settings in mm-broadband-modem-mbim.c */
+static void
+parent_att_hack_set_initial_eps_bearer_settings (MMIfaceModem3gpp    *_self,
+                                                 MMBearerProperties  *config,
+                                                 GAsyncReadyCallback  callback,
+                                                 gpointer             user_data)
+{
+    MMSharedFibocom *self = MM_SHARED_FIBOCOM (_self);
+    GTask                *task;
+    MbimDevice           *device;
+    MbimMessage          *message;
+
+    if (!peek_device (MM_BROADBAND_MODEM_MBIM (self), &device, callback, user_data))
+        return;
+
+    task = g_task_new (self, NULL, callback, user_data);
+
+    if (!mm_broadband_modem_mbim_get_is_lte_attach_info_supported(MM_BROADBAND_MODEM_MBIM (self))) {
+        g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+                                 "LTE attach configuration is unsupported");
+        g_object_unref (task);
+        return;
+    }
+
+    g_task_set_task_data (task, g_object_ref (config), g_object_unref);
+
+    message = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_query_new (NULL);
+    mbim_device_command (device,
+                         message,
+                         10,
+                         NULL,
+                         (GAsyncReadyCallback)parent_att_hack_before_set_lte_attach_configuration_query_ready,
+                         task);
+    mbim_message_unref (message);
+}
+
 static void
 parent_set_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self,
                                               GAsyncResult     *res,
@@ -158,6 +363,9 @@
     MMSharedFibocom                    *self;
     SetInitialEpsBearerSettingsContext *ctx;
     Private                            *priv;
+    g_autoptr(MMBaseSim)                modem_sim = NULL;
+    gchar                              *apn;
+    gchar                              *operator_identifier = NULL;
 
     self = g_task_get_source_object (task);
     ctx  = g_task_get_task_data (task);
@@ -167,6 +375,24 @@
     g_assert (priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings);
     g_assert (priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings_finish);
 
+    g_object_get (self,
+                  MM_IFACE_MODEM_SIM, &modem_sim,
+                  NULL);
+    if (modem_sim)
+        operator_identifier = mm_gdbus_sim_get_operator_identifier(modem_sim);
+    apn = mm_bearer_properties_get_apn (ctx->config);
+    mm_obj_info (self, "operator_identifier: '%s' apn='%s'", operator_identifier, apn);
+    if (mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)) == 0x2cb7 &&
+        mm_base_modem_get_product_id (MM_BASE_MODEM (self)) == 0x0007 &&
+        operator_identifier && g_strcmp0(operator_identifier, "310280") == 0) {
+        mm_obj_info (self, "executing custom attach logic for AT&T 310280");
+        parent_att_hack_set_initial_eps_bearer_settings (MM_IFACE_MODEM_3GPP (self),
+                                                         ctx->config,
+                                                         (GAsyncReadyCallback)parent_set_initial_eps_bearer_settings_ready,
+                                                         task);
+        return;
+    }
+
     priv->iface_modem_3gpp_parent->set_initial_eps_bearer_settings (MM_IFACE_MODEM_3GPP (self),
                                                                     ctx->config,
                                                                     (GAsyncReadyCallback)parent_set_initial_eps_bearer_settings_ready,
diff --git a/src/plugins/mtk/77-mm-mtk-port-types.rules b/src/plugins/mtk/77-mm-mtk-port-types.rules
index 9dba3e7..6f320c3 100644
--- a/src/plugins/mtk/77-mm-mtk-port-types.rules
+++ b/src/plugins/mtk/77-mm-mtk-port-types.rules
@@ -5,10 +5,11 @@
 
 LABEL="mm_mtk_port_types"
 
+# Fibocom FM350: ChromeOS specific configuration
+#  mbim0at0: AT port, ignored.
+ATTRS{vendor}=="0x14c3", ATTRS{device}=="0x4d75", ATTR{type}=="AT", ENV{ID_MM_PORT_IGNORE}="1"
+
 # Fibocom FM350 attach APN with toggle modem power
 ATTRS{vendor}=="0x14c3", ATTRS{device}=="0x4d75", ENV{ID_MM_FIBOCOM_INITIAL_EPS_OFF_ON}="1"
 
-# Fibocom FM350 doesn't correctly support multiplexing yet
-ATTRS{vendor}=="0x14c3", ATTRS{device}=="0x4d75", ENV{ID_MM_MAX_MULTIPLEXED_LINKS}="0"
-
-LABEL="mm_mtk_port_types_end"
\ No newline at end of file
+LABEL="mm_mtk_port_types_end"
diff --git a/src/plugins/qcom-soc/77-mm-qcom-soc.rules b/src/plugins/qcom-soc/77-mm-qcom-soc.rules
index 9719f96..8320a85 100644
--- a/src/plugins/qcom-soc/77-mm-qcom-soc.rules
+++ b/src/plugins/qcom-soc/77-mm-qcom-soc.rules
@@ -37,4 +37,7 @@
 # flag all rpmsg ports under this plugin as candidate
 KERNEL=="rpmsg*", SUBSYSTEM=="rpmsg", ENV{ID_MM_CANDIDATE}="1"
 
+# TODO(b/175305412): flag rmnet_data0 port under this plugin as candidate.
+KERNEL=="rmnet_data0", SUBSYSTEM=="net", ENV{ID_MM_CANDIDATE}="1"
+
 LABEL="mm_qcom_soc_end"
diff --git a/src/plugins/tests/test-fixture.c b/src/plugins/tests/test-fixture.c
index 29eb8d5..ac2d3e6 100644
--- a/src/plugins/tests/test-fixture.c
+++ b/src/plugins/tests/test-fixture.c
@@ -142,7 +142,7 @@
             break;
 
         /* Blocking wait */
-        g_assert_cmpuint (wait_time, <=, 20);
+        g_assert_cmpuint (wait_time, <=, 120);
         wait_time++;
         sleep (1);
     }
diff --git a/src/tests/test-qcdm-serial-port.c b/src/tests/test-qcdm-serial-port.c
index fff345d..bf8ff4b 100644
--- a/src/tests/test-qcdm-serial-port.c
+++ b/src/tests/test-qcdm-serial-port.c
@@ -444,10 +444,13 @@
 {
     g_test_init (&argc, &argv, NULL);
 
-    TESTCASE_PTY ("/MM/QCDM/Verinfo", test_verinfo);
-    TESTCASE_PTY ("/MM/QCDM/Sierra-Cns-Rejected", test_sierra_cns_rejected);
-    TESTCASE_PTY ("/MM/QCDM/Random-Data-Rejected", test_random_data_rejected);
-    TESTCASE_PTY ("/MM/QCDM/Leading-Frame-Markers", test_leading_frame_markers);
+    // Disabled in ChromiumOS because of flakiness.
+    // TODO(b/172214192); Re-enabled them once
+    // https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/184 is resolved
+    // TESTCASE_PTY ("/MM/QCDM/Verinfo", test_verinfo);
+    // TESTCASE_PTY ("/MM/QCDM/Sierra-Cns-Rejected", test_sierra_cns_rejected);
+    // TESTCASE_PTY ("/MM/QCDM/Random-Data-Rejected", test_random_data_rejected);
+    // TESTCASE_PTY ("/MM/QCDM/Leading-Frame-Markers", test_leading_frame_markers);
 
     return g_test_run ();
 }
diff --git a/unblocked_terms.txt b/unblocked_terms.txt
new file mode 100644
index 0000000..cc0e0cd
--- /dev/null
+++ b/unblocked_terms.txt
@@ -0,0 +1,12 @@
+# Don't delete this file if you want to keep keyword_check enabled even if it's
+# empty.
+# See repohooks/README.md for more details.
+black.?list
+dummy
+gr[ae]y.?list
+\bhe\b
+\bshe\b
+master
+\bnative
+slave
+white.?list