ICU-20568 Implement Usage "Glue Code" and skeleton support. Tests.

CLDR-13488: Samples of intermediate source/data/misc/units.txt
PR: https://github.com/icu-units/icu/pull/5
Commit: 08132e7a3388e055590e5ba60ba37c9b5a87b900

Unit test improvements (the rest of PR already upstreamed).
PR: https://github.com/sffc/icu/pull/40
Commit: e92a2fcffbbcb92868954f28e3366568e6b3cc7c

Use namespace ::icu::units::impl for units-related code.
PR: https://github.com/icu-units/icu/pull/16
Commit: 57b35a8a93e751aa73a190e63da494d24fd67bef

Export addSingleFactorConstant in header file
PR: https://github.com/icu-units/icu/pull/17
Commit: f357b5558ebc2f0aab53886492beae7f17d9c3cc

Fix MeasureUnit identifiers to be spec-compliant.
PR: https://github.com/icu-units/icu/pull/18
Commit: 69c087154b871b53fdba978bfa1b1ba4c4e71594

Cleanup: IWYU headers (with some exceptions), SigNum -> Signum
PR: https://github.com/icu-units/icu/pull/19
Commit: 1c29a6bcc74c113a7d51b008a93cd7986437c841

Test that addSingleFactorConstant knowns all unitConstants.
PR: https://github.com/icu-units/icu/pull/20
Commit: 6c0c3bb64080354ea109d1356b98ac7a3a97f443

Add Unit Usage support to Number Skeletons.
PR: https://github.com/icu-units/icu/pull/33
Commit: 3cf23222ca8c7fa394e3ed37ec8333f5e86c6807

Fix some compiler warnings
PR: https://github.com/icu-units/icu/pull/43
Commit: be11edb0bd70a6857ea24e7427725b803263481d
diff --git a/icu4c/source/i18n/complexunitsconverter.cpp b/icu4c/source/i18n/complexunitsconverter.cpp
index 4a77b79..4890f74 100644
--- a/icu4c/source/i18n/complexunitsconverter.cpp
+++ b/icu4c/source/i18n/complexunitsconverter.cpp
@@ -6,15 +6,17 @@
 #if !UCONFIG_NO_FORMATTING
 
 #include <math.h>
-#include <utility>
 
 #include "cmemory.h"
 #include "complexunitsconverter.h"
 #include "uassert.h"
 #include "unicode/fmtable.h"
+#include "unicode/localpointer.h"
+#include "unicode/measure.h"
 #include "unitconverter.h"
 
 U_NAMESPACE_BEGIN
+namespace units {
 
 ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnit &inputUnit,
                                              const MeasureUnit &outputUnits,
@@ -123,6 +125,7 @@
     return result;
 }
 
+} // namespace units
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/complexunitsconverter.h b/icu4c/source/i18n/complexunitsconverter.h
index 0fe76ba..fbae2b1 100644
--- a/icu4c/source/i18n/complexunitsconverter.h
+++ b/icu4c/source/i18n/complexunitsconverter.h
@@ -8,14 +8,17 @@
 #define __COMPLEXUNITSCONVERTER_H__
 
 #include "cmemory.h"
-#include "unicode/errorcode.h"
 #include "unicode/measunit.h"
-#include "unicode/measure.h"
 #include "unitconverter.h"
 #include "unitsdata.h"
 
 U_NAMESPACE_BEGIN
 
+// Forward declarations
+class Measure;
+
+namespace units {
+
 /**
  *  Converts from single or compound unit to single, compound or mixed units.
  * For example, from `meter` to `foot+inch`.
@@ -60,6 +63,7 @@
     MaybeStackVector<MeasureUnit> units_;
 };
 
+} // namespace units
 U_NAMESPACE_END
 
 #endif //__COMPLEXUNITSCONVERTER_H__
diff --git a/icu4c/source/i18n/formatted_string_builder.h b/icu4c/source/i18n/formatted_string_builder.h
index 4567dc1..4a886b9 100644
--- a/icu4c/source/i18n/formatted_string_builder.h
+++ b/icu4c/source/i18n/formatted_string_builder.h
@@ -25,7 +25,7 @@
  *
  * <ol>
  * <li>Efficient prepend as well as append.
- * <li>Keeps tracks of Fields in an efficient manner.
+ * <li>Keeps track of Fields in an efficient manner.
  * </ol>
  *
  * See also FormattedValueStringBuilderImpl.
diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj
index ea139c0..cf5ea8c 100644
--- a/icu4c/source/i18n/i18n.vcxproj
+++ b/icu4c/source/i18n/i18n.vcxproj
@@ -196,6 +196,7 @@
     <ClCompile Include="number_rounding.cpp" />
     <ClCompile Include="number_scientific.cpp" />
     <ClCompile Include="formatted_string_builder.cpp" />
+    <ClCompile Include="number_usageprefs.cpp" />
     <ClCompile Include="number_utils.cpp" />
     <ClCompile Include="number_mapper.cpp" />
     <ClCompile Include="number_multiplier.cpp" />
@@ -469,6 +470,7 @@
     <ClInclude Include="number_scientific.h" />
     <ClInclude Include="formatted_string_builder.h" />
     <ClInclude Include="number_types.h" />
+    <ClCompile Include="number_usageprefs.h" />
     <ClInclude Include="number_utypes.h" />
     <ClInclude Include="number_utils.h" />
     <ClInclude Include="number_mapper.h" />
diff --git a/icu4c/source/i18n/i18n_uwp.vcxproj b/icu4c/source/i18n/i18n_uwp.vcxproj
index 30c98a0..2742efe 100644
--- a/icu4c/source/i18n/i18n_uwp.vcxproj
+++ b/icu4c/source/i18n/i18n_uwp.vcxproj
@@ -429,11 +429,13 @@
     <ClCompile Include="number_rounding.cpp" />
     <ClCompile Include="number_scientific.cpp" />
     <ClCompile Include="formatted_string_builder.cpp" />
+    <ClCompile Include="number_usageprefs.cpp" />
     <ClCompile Include="number_utils.cpp" />
     <ClCompile Include="number_mapper.cpp" />
     <ClCompile Include="number_multiplier.cpp" />
     <ClCompile Include="number_currencysymbols.cpp" />
     <ClCompile Include="number_skeletons.cpp" />
+    <ClCompile Include="number_symbolswrapper.cpp" />
     <ClCompile Include="number_capi.cpp" />
     <ClCompile Include="string_segment.cpp" />
     <ClCompile Include="numparse_parsednumber.cpp" />
@@ -700,12 +702,14 @@
     <ClInclude Include="number_scientific.h" />
     <ClInclude Include="formatted_string_builder.h" />
     <ClInclude Include="number_types.h" />
+    <ClInclude Include="number_usageprefs.h" />
     <ClInclude Include="number_utypes.h" />
     <ClInclude Include="number_utils.h" />
     <ClInclude Include="number_mapper.h" />
     <ClInclude Include="number_multiplier.h" />
     <ClInclude Include="number_currencysymbols.h" />
     <ClInclude Include="number_skeletons.h" />
+    <ClInclude Include="number_symbolswrapper.h" />
     <ClInclude Include="string_segment.h" />
     <ClInclude Include="numparse_impl.h" />
     <ClInclude Include="numparse_symbols.h" />
diff --git a/icu4c/source/i18n/number_decimalquantity.h b/icu4c/source/i18n/number_decimalquantity.h
index d9b35c0..bfc908f 100644
--- a/icu4c/source/i18n/number_decimalquantity.h
+++ b/icu4c/source/i18n/number_decimalquantity.h
@@ -20,7 +20,7 @@
 class DecNum;
 
 /**
- * An class for representing a number to be processed by the decimal formatting pipeline. Includes
+ * A class for representing a number to be processed by the decimal formatting pipeline. Includes
  * methods for rounding, plural rules, and decimal digit extraction.
  *
  * <p>By design, this is NOT IMMUTABLE and NOT THREAD SAFE. It is intended to be an intermediate
@@ -217,7 +217,13 @@
 
     DecimalQuantity &setToDouble(double n);
 
-    /** decNumber is similar to BigDecimal in Java. */
+    /**
+     * Produces a DecimalQuantity that was parsed from a string by the decNumber
+     * C Library.
+     *
+     * decNumber is similar to BigDecimal in Java, and supports parsing strings
+     * such as "123.456621E+40".
+     */
     DecimalQuantity &setToDecNumber(StringPiece n, UErrorCode& status);
 
     /** Internal method if the caller already has a DecNum. */
diff --git a/icu4c/source/i18n/number_fluent.cpp b/icu4c/source/i18n/number_fluent.cpp
index da8cc7e..579f306 100644
--- a/icu4c/source/i18n/number_fluent.cpp
+++ b/icu4c/source/i18n/number_fluent.cpp
@@ -275,6 +275,20 @@
 }
 
 template<typename Derived>
+Derived NumberFormatterSettings<Derived>::usage(const StringPiece usage) const& {
+    Derived copy(*this);
+    copy.fMacros.usage.set(usage);
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberFormatterSettings<Derived>::usage(const StringPiece usage)&& {
+    Derived move(std::move(*this));
+    move.fMacros.usage.set(usage);
+    return move;
+}
+
+template<typename Derived>
 Derived NumberFormatterSettings<Derived>::padding(const Padder& padder) const& {
     Derived copy(*this);
     copy.fMacros.padder = padder;
@@ -702,9 +716,9 @@
 
 void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, UErrorCode& status) const {
     if (computeCompiled(status)) {
-        fCompiled->format(results->quantity, results->getStringRef(), status);
+        fCompiled->format(results, status);
     } else {
-        NumberFormatterImpl::formatStatic(fMacros, results->quantity, results->getStringRef(), status);
+        NumberFormatterImpl::formatStatic(fMacros, results, status);
     }
     if (U_FAILURE(status)) {
         return;
diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp
index 875f71b..c047a1f 100644
--- a/icu4c/source/i18n/number_formatimpl.cpp
+++ b/icu4c/source/i18n/number_formatimpl.cpp
@@ -32,13 +32,16 @@
     : NumberFormatterImpl(macros, true, status) {
 }
 
-int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue,
-                                       FormattedStringBuilder& outString, UErrorCode& status) {
+int32_t NumberFormatterImpl::formatStatic(const MacroProps &macros, UFormattedNumberData *results,
+                                          UErrorCode &status) {
+    DecimalQuantity &inValue = results->quantity;
+    FormattedStringBuilder &outString = results->getStringRef();
     NumberFormatterImpl impl(macros, false, status);
     MicroProps& micros = impl.preProcessUnsafe(inValue, status);
     if (U_FAILURE(status)) { return 0; }
     int32_t length = writeNumber(micros, inValue, outString, 0, status);
     length += writeAffixes(micros, outString, 0, length, status);
+    results->outputUnit = std::move(micros.outputUnit);
     return length;
 }
 
@@ -54,13 +57,15 @@
 // The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
 // See MicroProps::processQuantity() for details.
 
-int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, FormattedStringBuilder& outString,
-                                UErrorCode& status) const {
+int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const {
+    DecimalQuantity &inValue = results->quantity;
+    FormattedStringBuilder &outString = results->getStringRef();
     MicroProps micros;
     preProcess(inValue, micros, status);
     if (U_FAILURE(status)) { return 0; }
     int32_t length = writeNumber(micros, inValue, outString, 0, status);
     length += writeAffixes(micros, outString, 0, length, status);
+    results->outputUnit = std::move(micros.outputUnit);
     return length;
 }
 
@@ -233,6 +238,19 @@
     /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
     /////////////////////////////////////////////////////////////////////////////////////
 
+    // Unit Preferences and Conversions as our first step
+    if (macros.usage.isSet()) {
+        if (!isCldrUnit) {
+            // We only support "usage" when the input unit is a CLDR Unit.
+            status = U_ILLEGAL_ARGUMENT_ERROR;
+            return nullptr;
+        }
+        auto usagePrefsHandler =
+            new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fUsage, chain, status);
+        fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);
+        chain = fUsagePrefsHandler.getAlias();
+    }
+
     // Multiplier
     if (macros.scale.isValid()) {
         fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
@@ -341,7 +359,8 @@
         patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);
     }
     if (safe) {
-        fImmutablePatternModifier.adoptInstead(patternModifier->createImmutable(status));
+        fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),
+                                                                status);
     }
     if (U_FAILURE(status)) {
         return nullptr;
@@ -349,24 +368,26 @@
 
     // Outer modifier (CLDR units and currency long names)
     if (isCldrUnit) {
-        fLongNameHandler.adoptInstead(
-                LongNameHandler::forMeasureUnit(
-                        macros.locale,
-                        macros.unit,
-                        macros.perUnit,
-                        unitWidth,
-                        resolvePluralRules(macros.rules, macros.locale, status),
-                        chain,
-                        status));
-        chain = fLongNameHandler.getAlias();
+        if (macros.usage.isSet()) {
+            fLongNameMultiplexer.adoptInsteadAndCheckErrorCode(
+                LongNameMultiplexer::forMeasureUnits(
+                    macros.locale, *fUsagePrefsHandler->getOutputUnits(), unitWidth,
+                    resolvePluralRules(macros.rules, macros.locale, status), chain, status),
+                status);
+            chain = fLongNameMultiplexer.getAlias();
+        } else {
+            fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status);
+            LongNameHandler::forMeasureUnit(macros.locale, macros.unit, macros.perUnit, unitWidth,
+                                            resolvePluralRules(macros.rules, macros.locale, status),
+                                            chain, fLongNameHandler.getAlias(), status);
+            chain = fLongNameHandler.getAlias();
+        }
     } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
-        fLongNameHandler.adoptInstead(
-                LongNameHandler::forCurrencyLongNames(
-                        macros.locale,
-                        currency,
-                        resolvePluralRules(macros.rules, macros.locale, status),
-                        chain,
-                        status));
+        fLongNameHandler.adoptInsteadAndCheckErrorCode(
+            LongNameHandler::forCurrencyLongNames(
+                macros.locale, currency, resolvePluralRules(macros.rules, macros.locale, status), chain,
+                status),
+            status);
         chain = fLongNameHandler.getAlias();
     } else {
         // No outer modifier required
@@ -390,6 +411,9 @@
             safe,
             chain,
             status);
+        if (U_FAILURE(status)) {
+            return nullptr;
+        }
         if (newCompactHandler == nullptr) {
             status = U_MEMORY_ALLOCATION_ERROR;
             return nullptr;
diff --git a/icu4c/source/i18n/number_formatimpl.h b/icu4c/source/i18n/number_formatimpl.h
index 084bc4a..062191a 100644
--- a/icu4c/source/i18n/number_formatimpl.h
+++ b/icu4c/source/i18n/number_formatimpl.h
@@ -10,11 +10,13 @@
 #include "number_types.h"
 #include "formatted_string_builder.h"
 #include "number_patternstring.h"
+#include "number_usageprefs.h"
 #include "number_utils.h"
 #include "number_patternmodifier.h"
 #include "number_longnames.h"
 #include "number_compact.h"
 #include "number_microprops.h"
+#include "number_utypes.h"
 
 U_NAMESPACE_BEGIN namespace number {
 namespace impl {
@@ -34,9 +36,8 @@
     /**
      * Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once.
      */
-    static int32_t
-    formatStatic(const MacroProps &macros, DecimalQuantity &inValue, FormattedStringBuilder &outString,
-                 UErrorCode &status);
+    static int32_t formatStatic(const MacroProps &macros, UFormattedNumberData *results,
+                                UErrorCode &status);
 
     /**
      * Prints only the prefix and suffix; used for DecimalFormat getters.
@@ -51,7 +52,7 @@
     /**
      * Evaluates the "safe" MicroPropsGenerator created by "fromMacros".
      */
-    int32_t format(DecimalQuantity& inValue, FormattedStringBuilder& outString, UErrorCode& status) const;
+    int32_t format(UFormattedNumberData *results, UErrorCode &status) const;
 
     /**
      * Like format(), but saves the result into an output MicroProps without additional processing.
@@ -82,7 +83,9 @@
                                 int32_t end, UErrorCode& status);
 
   private:
-    // Head of the MicroPropsGenerator linked list:
+    // Head of the MicroPropsGenerator linked list. Subclasses' processQuantity
+    // methods process this list in a parent-first order, such that the last
+    // item added, which this points to, typically has its logic executed last.
     const MicroPropsGenerator *fMicroPropsGenerator = nullptr;
 
     // Tail of the list:
@@ -90,13 +93,15 @@
 
     // Other fields possibly used by the number formatting pipeline:
     // TODO: Convert more of these LocalPointers to value objects to reduce the number of news?
+    LocalPointer<const UsagePrefsHandler> fUsagePrefsHandler;
     LocalPointer<const DecimalFormatSymbols> fSymbols;
     LocalPointer<const PluralRules> fRules;
     LocalPointer<const ParsedPatternInfo> fPatternInfo;
     LocalPointer<const ScientificHandler> fScientificHandler;
     LocalPointer<MutablePatternModifier> fPatternModifier;
     LocalPointer<ImmutablePatternModifier> fImmutablePatternModifier;
-    LocalPointer<const LongNameHandler> fLongNameHandler;
+    LocalPointer<LongNameHandler> fLongNameHandler;
+    LocalPointer<const LongNameMultiplexer> fLongNameMultiplexer;
     LocalPointer<const CompactHandler> fCompactHandler;
 
     // Value objects possibly used by the number formatting pipeline:
diff --git a/icu4c/source/i18n/number_longnames.cpp b/icu4c/source/i18n/number_longnames.cpp
index bb32d03..7ace7fd 100644
--- a/icu4c/source/i18n/number_longnames.cpp
+++ b/icu4c/source/i18n/number_longnames.cpp
@@ -22,7 +22,21 @@
 
 namespace {
 
+/**
+ * Display Name (this format has no placeholder).
+ *
+ * Used as an index into the LongNameHandler::simpleFormats array. Units
+ * resources cover the normal set of PluralRules keys, as well as `dnam` and
+ * `per` forms.
+ */
 constexpr int32_t DNAM_INDEX = StandardPlural::Form::COUNT;
+/**
+ * "per" form (e.g. "{0} per day" is day's "per" form).
+ *
+ * Used as an index into the LongNameHandler::simpleFormats array. Units
+ * resources cover the normal set of PluralRules keys, as well as `dnam` and
+ * `per` forms.
+ */
 constexpr int32_t PER_INDEX = StandardPlural::Form::COUNT + 1;
 constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 2;
 
@@ -87,6 +101,12 @@
 
 // NOTE: outArray MUST have room for all StandardPlural values.  No bounds checking is performed.
 
+// Populates outArray with `locale`-specific values for `unit` through use of
+// PluralTableSink, reading from resources *unitsNarrow* and *unitsShort* (for
+// width UNUM_UNIT_WIDTH_NARROW), or just *unitsShort* (for width
+// UNUM_UNIT_WIDTH_SHORT). For other widths, it would read just "units".
+//
+// outArray must be of fixed length ARRAY_LENGTH.
 void getMeasureData(const Locale &locale, const MeasureUnit &unit, const UNumberUnitWidth &width,
                     UnicodeString *outArray, UErrorCode &status) {
     PluralTableSink sink(outArray);
@@ -184,14 +204,19 @@
 
 } // namespace
 
-LongNameHandler*
-LongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &unitRef, const MeasureUnit &perUnit,
-                                const UNumberUnitWidth &width, const PluralRules *rules,
-                                const MicroPropsGenerator *parent, UErrorCode &status) {
+// TODO(units,hugovdm): deal properly with "perUnit" parameter here:
+void LongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &unitRef,
+                                     const MeasureUnit &perUnit, const UNumberUnitWidth &width,
+                                     const PluralRules *rules, const MicroPropsGenerator *parent,
+                                     LongNameHandler *fillIn, UErrorCode &status) {
+    if (fillIn == nullptr) {
+        status = U_INTERNAL_PROGRAM_ERROR;
+        return;
+    }
     if (uprv_strlen(unitRef.getType()) == 0 || uprv_strlen(perUnit.getType()) == 0) {
         // TODO(ICU-20941): Unsanctioned unit. Not yet fully supported. Set an error code.
         status = U_UNSUPPORTED_ERROR;
-        return nullptr;
+        return;
     }
 
     MeasureUnit unit = unitRef;
@@ -203,59 +228,75 @@
             unit = resolved;
         } else {
             // No simplified form is available.
-            return forCompoundUnit(loc, unit, perUnit, width, rules, parent, status);
+            forCompoundUnit(loc, unit, perUnit, width, rules, parent, fillIn, status);
+            return;
         }
     }
 
-    auto* result = new LongNameHandler(rules, parent);
-    if (result == nullptr) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return nullptr;
-    }
     UnicodeString simpleFormats[ARRAY_LENGTH];
     getMeasureData(loc, unit, width, simpleFormats, status);
-    if (U_FAILURE(status)) { return result; }
-    result->simpleFormatsToModifiers(simpleFormats, {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status);
-    return result;
+    if (U_FAILURE(status)) {
+        return;
+    }
+    fillIn->rules = rules;
+    fillIn->parent = parent;
+    fillIn->simpleFormatsToModifiers(simpleFormats, {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD},
+                                     status);
 }
 
-LongNameHandler*
-LongNameHandler::forCompoundUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
-                                 const UNumberUnitWidth &width, const PluralRules *rules,
-                                 const MicroPropsGenerator *parent, UErrorCode &status) {
-    auto* result = new LongNameHandler(rules, parent);
-    if (result == nullptr) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return nullptr;
+// TODO(units,hugovdm): deal properly with "perUnit" parameter here:
+void LongNameHandler::forCompoundUnit(const Locale &loc, const MeasureUnit &unit,
+                                      const MeasureUnit &perUnit, const UNumberUnitWidth &width,
+                                      const PluralRules *rules, const MicroPropsGenerator *parent,
+                                      LongNameHandler *fillIn, UErrorCode &status) {
+    if (fillIn == nullptr) {
+        status = U_INTERNAL_PROGRAM_ERROR;
+        return;
     }
     UnicodeString primaryData[ARRAY_LENGTH];
     getMeasureData(loc, unit, width, primaryData, status);
-    if (U_FAILURE(status)) { return result; }
+    if (U_FAILURE(status)) {
+        return;
+    }
     UnicodeString secondaryData[ARRAY_LENGTH];
     getMeasureData(loc, perUnit, width, secondaryData, status);
-    if (U_FAILURE(status)) { return result; }
+    if (U_FAILURE(status)) {
+        return;
+    }
 
     UnicodeString perUnitFormat;
     if (!secondaryData[PER_INDEX].isBogus()) {
         perUnitFormat = secondaryData[PER_INDEX];
     } else {
         UnicodeString rawPerUnitFormat = getPerUnitFormat(loc, width, status);
-        if (U_FAILURE(status)) { return result; }
+        if (U_FAILURE(status)) {
+            return;
+        }
         // rawPerUnitFormat is something like "{0}/{1}"; we need to substitute in the secondary unit.
         SimpleFormatter compiled(rawPerUnitFormat, 2, 2, status);
-        if (U_FAILURE(status)) { return result; }
+        if (U_FAILURE(status)) {
+            return;
+        }
         UnicodeString secondaryFormat = getWithPlural(secondaryData, StandardPlural::Form::ONE, status);
-        if (U_FAILURE(status)) { return result; }
+        if (U_FAILURE(status)) {
+            return;
+        }
         // Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
         SimpleFormatter secondaryCompiled(secondaryFormat, 0, 1, status);
-        if (U_FAILURE(status)) { return result; }
+        if (U_FAILURE(status)) {
+            return;
+        }
         UnicodeString secondaryString = secondaryCompiled.getTextWithNoArguments().trim();
         // TODO: Why does UnicodeString need to be explicit in the following line?
         compiled.format(UnicodeString(u"{0}"), secondaryString, perUnitFormat, status);
-        if (U_FAILURE(status)) { return result; }
+        if (U_FAILURE(status)) {
+            return;
+        }
     }
-    result->multiSimpleFormatsToModifiers(primaryData, perUnitFormat, {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status);
-    return result;
+    fillIn->rules = rules;
+    fillIn->parent = parent;
+    fillIn->multiSimpleFormatsToModifiers(primaryData, perUnitFormat,
+                                          {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status);
 }
 
 UnicodeString LongNameHandler::getUnitDisplayName(
@@ -338,7 +379,9 @@
 
 void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
                                       UErrorCode &status) const {
-    parent->processQuantity(quantity, micros, status);
+    if (parent != NULL) {
+        parent->processQuantity(quantity, micros, status);
+    }
     StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status);
     micros.modOuter = &fModifiers[pluralForm];
 }
@@ -347,4 +390,49 @@
     return &fModifiers[plural];
 }
 
+LongNameMultiplexer *
+LongNameMultiplexer::forMeasureUnits(const Locale &loc, const MaybeStackVector<MeasureUnit> &units,
+                                     const UNumberUnitWidth &width, const PluralRules *rules,
+                                     const MicroPropsGenerator *parent, UErrorCode &status) {
+    LocalPointer<LongNameMultiplexer> result(new LongNameMultiplexer(parent), status);
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
+    U_ASSERT(units.length() > 0);
+    result->fMeasureUnits.adoptInstead(new MeasureUnit[units.length()]);
+    for (int32_t i = 0, length = units.length(); i < length; i++) {
+        // Create empty new LongNameHandler:
+        LongNameHandler *lnh =
+            result->fLongNameHandlers.emplaceBackAndCheckErrorCode(status);
+        result->fMeasureUnits[i] = *units[i];
+        // Fill in LongNameHandler:
+        LongNameHandler::forMeasureUnit(loc, *units[i],
+                                        MeasureUnit(), // TODO(units): deal with COMPOUND and MIXED units
+                                        width, rules, NULL, lnh, status);
+        if (U_FAILURE(status)) {
+            return nullptr;
+        }
+    }
+    return result.orphan();
+}
+
+void LongNameMultiplexer::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
+                                          UErrorCode &status) const {
+    // We call parent->processQuantity() from the Multiplexer, instead of
+    // letting LongNameHandler handle it: we don't know which LongNameHandler to
+    // call until we've called the parent!
+    fParent->processQuantity(quantity, micros, status);
+
+    // Call the correct LongNameHandler based on outputUnit
+    for (int i = 0; i < fLongNameHandlers.length(); i++) {
+        if (fMeasureUnits[i] == micros.outputUnit) {
+            fLongNameHandlers[i]->processQuantity(quantity, micros, status);
+            return;
+        }
+    }
+    // We shouldn't receive any outputUnit for which we haven't already got a
+    // LongNameHandler:
+    status = U_INTERNAL_PROGRAM_ERROR;
+}
+
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/number_longnames.h b/icu4c/source/i18n/number_longnames.h
index a19425a..db08c11 100644
--- a/icu4c/source/i18n/number_longnames.h
+++ b/icu4c/source/i18n/number_longnames.h
@@ -7,6 +7,7 @@
 #ifndef __NUMBER_LONGNAMES_H__
 #define __NUMBER_LONGNAMES_H__
 
+#include "cmemory.h"
 #include "unicode/uversion.h"
 #include "number_utils.h"
 #include "number_modifiers.h"
@@ -33,10 +34,10 @@
     forCurrencyLongNames(const Locale &loc, const CurrencyUnit &currency, const PluralRules *rules,
                          const MicroPropsGenerator *parent, UErrorCode &status);
 
-    static LongNameHandler*
-    forMeasureUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
-                   const UNumberUnitWidth &width, const PluralRules *rules,
-                   const MicroPropsGenerator *parent, UErrorCode &status);
+    static void forMeasureUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
+                               const UNumberUnitWidth &width, const PluralRules *rules,
+                               const MicroPropsGenerator *parent, LongNameHandler *fillIn,
+                               UErrorCode &status);
 
     void
     processQuantity(DecimalQuantity &quantity, MicroProps &micros, UErrorCode &status) const U_OVERRIDE;
@@ -45,22 +46,63 @@
 
   private:
     SimpleModifier fModifiers[StandardPlural::Form::COUNT];
+    // Not owned
     const PluralRules *rules;
+    // Not owned
     const MicroPropsGenerator *parent;
 
     LongNameHandler(const PluralRules *rules, const MicroPropsGenerator *parent)
-            : rules(rules), parent(parent) {}
+        : rules(rules), parent(parent) {
+    }
 
-    static LongNameHandler*
-    forCompoundUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
-                    const UNumberUnitWidth &width, const PluralRules *rules,
-                    const MicroPropsGenerator *parent, UErrorCode &status);
+    LongNameHandler() : rules(nullptr), parent(nullptr) {
+    }
+
+    friend class MemoryPool<LongNameHandler>; // To enable emplaceBack();
+    friend class NumberFormatterImpl;
+
+    static void forCompoundUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
+                                const UNumberUnitWidth &width, const PluralRules *rules,
+                                const MicroPropsGenerator *parent, LongNameHandler *fillIn,
+                                UErrorCode &status);
 
     void simpleFormatsToModifiers(const UnicodeString *simpleFormats, Field field, UErrorCode &status);
     void multiSimpleFormatsToModifiers(const UnicodeString *leadFormats, UnicodeString trailFormat,
                                        Field field, UErrorCode &status);
 };
 
+const int MAX_PREFS_COUNT = 10;
+
+/**
+ * A MicroPropsGenerator that multiplexes between different LongNameHandlers,
+ * depending on the outputUnit (micros.helpers.outputUnit should be set earlier
+ * in the chain).
+ */
+class LongNameMultiplexer : public MicroPropsGenerator, public UMemory {
+  public:
+    // FIXME: docstring?
+    static LongNameMultiplexer *forMeasureUnits(const Locale &loc,
+                                                const MaybeStackVector<MeasureUnit> &units,
+                                                const UNumberUnitWidth &width, const PluralRules *rules,
+                                                const MicroPropsGenerator *parent, UErrorCode &status);
+
+    void processQuantity(DecimalQuantity &quantity, MicroProps &micros,
+                         UErrorCode &status) const U_OVERRIDE;
+
+  private:
+    /**
+     * Because we only know which LongNameHandler we wish to call after calling
+     * earlier MicroPropsGenerators in the chain, LongNameMultiplexer keeps the
+     * parent link, while the LongNameHandlers are given no parents.
+     */
+    MaybeStackVector<LongNameHandler> fLongNameHandlers;
+    LocalArray<MeasureUnit> fMeasureUnits;
+    const MicroPropsGenerator *fParent;
+
+    LongNameMultiplexer(const MicroPropsGenerator *parent) : fParent(parent) {
+    }
+};
+
 }  // namespace impl
 }  // namespace number
 U_NAMESPACE_END
diff --git a/icu4c/source/i18n/number_microprops.h b/icu4c/source/i18n/number_microprops.h
index 56512f5..cb68c41 100644
--- a/icu4c/source/i18n/number_microprops.h
+++ b/icu4c/source/i18n/number_microprops.h
@@ -22,6 +22,11 @@
 U_NAMESPACE_BEGIN namespace number {
 namespace impl {
 
+// TODO(units): generated by MicroPropsGenerator, but inherits from it too. Do
+// we want to better document why? There's an explanation for processQuantity:
+//  * As MicroProps is the "base instance", this implementation of
+//  * MicroPropsGenerator::processQuantity() just ensures that the output
+//  * `micros` is correctly initialized.
 struct MicroProps : public MicroPropsGenerator {
 
     // NOTE: All of these fields are properly initialized in NumberFormatterImpl.
@@ -49,6 +54,8 @@
         MultiplierFormatHandler multiplier;
     } helpers;
 
+    // The MeasureUnit with which the output measurement is represented.
+    MeasureUnit outputUnit;
 
     MicroProps() = default;
 
@@ -56,7 +63,23 @@
 
     MicroProps& operator=(const MicroProps& other) = default;
 
-    void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE {
+    /**
+     * As MicroProps is the "base instance", this implementation of
+     * MicroPropsGenerator::processQuantity() just ensures that the output
+     * `micros` is correctly initialized.
+     *
+     * For the "safe" invocation of this function, micros must not be *this,
+     * such that a copy of the base instance is made. For the "unsafe" path,
+     * this function can be used only once, because the base MicroProps instance
+     * will be modified and thus not be available for re-use.
+     *
+     * @param quantity The quantity for consideration and optional mutation.
+     * @param micros The MicroProps instance to populate. If this parameter is
+     * not already `*this`, it will be overwritten with a copy of `*this`.
+     */
+    void processQuantity(DecimalQuantity &quantity, MicroProps &micros,
+                         UErrorCode &status) const U_OVERRIDE {
+        (void) quantity;
         (void) status;
         if (this == &micros) {
             // Unsafe path: no need to perform a copy.
@@ -65,6 +88,7 @@
             U_ASSERT(exhausted);
         } else {
             // Safe path: copy self into the output micros.
+            U_ASSERT(!exhausted);
             micros = *this;
         }
     }
diff --git a/icu4c/source/i18n/number_output.cpp b/icu4c/source/i18n/number_output.cpp
index 40192a9..9b1a11d 100644
--- a/icu4c/source/i18n/number_output.cpp
+++ b/icu4c/source/i18n/number_output.cpp
@@ -5,6 +5,7 @@
 
 #if !UCONFIG_NO_FORMATTING
 
+#include "unicode/measunit.h"
 #include "unicode/numberformatter.h"
 #include "number_utypes.h"
 #include "util.h"
@@ -32,6 +33,11 @@
     fData->getAllFieldPositions(fpih, status);
 }
 
+MeasureUnit FormattedNumber::getOutputUnit(UErrorCode& status) const {
+    UPRV_FORMATTED_VALUE_METHOD_GUARD(MeasureUnit())
+    return fData->outputUnit;
+}
+
 void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const {
     UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG)
     output = fData->quantity;
diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp
index dd163f8..7c2a759 100644
--- a/icu4c/source/i18n/number_skeletons.cpp
+++ b/icu4c/source/i18n/number_skeletons.cpp
@@ -99,6 +99,7 @@
     b.add(u"measure-unit", STEM_MEASURE_UNIT, status);
     b.add(u"per-measure-unit", STEM_PER_MEASURE_UNIT, status);
     b.add(u"unit", STEM_UNIT, status);
+    b.add(u"usage", STEM_UNIT_USAGE, status);
     b.add(u"currency", STEM_CURRENCY, status);
     b.add(u"integer-width", STEM_INTEGER_WIDTH, status);
     b.add(u"numbering-system", STEM_NUMBERING_SYSTEM, status);
@@ -559,6 +560,7 @@
                 case STATE_MEASURE_UNIT:
                 case STATE_PER_MEASURE_UNIT:
                 case STATE_IDENTIFIER_UNIT:
+                case STATE_UNIT_USAGE:
                 case STATE_CURRENCY_UNIT:
                 case STATE_INTEGER_WIDTH:
                 case STATE_NUMBERING_SYSTEM:
@@ -716,7 +718,7 @@
             macros.decimal = stem_to_object::decimalSeparatorDisplay(stem);
             return STATE_NULL;
 
-            // Stems requiring an option:
+        // Stems requiring an option:
 
         case STEM_PRECISION_INCREMENT:
             CHECK_NULL(seen, precision, status);
@@ -735,6 +737,10 @@
             CHECK_NULL(seen, perUnit, status);
             return STATE_IDENTIFIER_UNIT;
 
+        case STEM_UNIT_USAGE:
+            CHECK_NULL(seen, usage, status);
+            return STATE_UNIT_USAGE;
+
         case STEM_CURRENCY:
             CHECK_NULL(seen, unit, status);
             return STATE_CURRENCY_UNIT;
@@ -774,6 +780,9 @@
         case STATE_IDENTIFIER_UNIT:
             blueprint_helpers::parseIdentifierUnitOption(segment, macros, status);
             return STATE_NULL;
+        case STATE_UNIT_USAGE:
+            blueprint_helpers::parseUnitUsageOption(segment, macros, status);
+            return STATE_NULL;
         case STATE_INCREMENT_PRECISION:
             blueprint_helpers::parseIncrementOption(segment, macros, status);
             return STATE_NULL;
@@ -848,6 +857,10 @@
         sb.append(u' ');
     }
     if (U_FAILURE(status)) { return; }
+    if (GeneratorHelpers::usage(macros, sb, status)) {
+        sb.append(u' ');
+    }
+    if (U_FAILURE(status)) { return; }
     if (GeneratorHelpers::precision(macros, sb, status)) {
         sb.append(u' ');
     }
@@ -1068,6 +1081,17 @@
     }
 }
 
+void blueprint_helpers::parseUnitUsageOption(const StringSegment &segment, MacroProps &macros,
+                                             UErrorCode &status) {
+    // Need to do char <-> UChar conversion...
+    U_ASSERT(U_SUCCESS(status));
+    CharString buffer;
+    SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
+    macros.usage.set(buffer.toStringPiece());
+    // We do not do any validation of the usage string: it depends on the
+    // unitPreferenceData in the units resources.
+}
+
 void blueprint_helpers::parseFractionStem(const StringSegment& segment, MacroProps& macros,
                                           UErrorCode& status) {
     U_ASSERT(segment.charAt(0) == u'.');
@@ -1549,6 +1573,15 @@
     }
 }
 
+bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode&) {
+    if (macros.usage.fLength > 0) {
+        sb.append(u"usage/", -1);
+        sb.append(UnicodeString(macros.usage.fUsage, -1, US_INV));
+        return true;
+    }
+    return false;
+}
+
 bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
     if (macros.precision.fType == Precision::RND_NONE) {
         sb.append(u"precision-unlimited", -1);
diff --git a/icu4c/source/i18n/number_skeletons.h b/icu4c/source/i18n/number_skeletons.h
index dd3813e..313c7ac 100644
--- a/icu4c/source/i18n/number_skeletons.h
+++ b/icu4c/source/i18n/number_skeletons.h
@@ -22,10 +22,12 @@
 // namespace for enums and entrypoint functions
 namespace skeleton {
 
-///////////////////////////////////////////////////////////////////////////////////////
-// NOTE: For an example of how to add a new stem to the number skeleton parser, see: //
-// http://bugs.icu-project.org/trac/changeset/41193                                  //
-///////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////
+// NOTE: For examples of how to add a new stem to the number skeleton parser, see:    //
+// https://github.com/unicode-org/icu/commit/a2a7982216b2348070dc71093775ac7195793d73 //
+// and                                                                                //
+// https://github.com/unicode-org/icu/commit/6fe86f3934a8a5701034f648a8f7c5087e84aa28 //
+////////////////////////////////////////////////////////////////////////////////////////
 
 /**
  * While parsing a skeleton, this enum records what type of option we expect to find next.
@@ -47,6 +49,7 @@
     STATE_MEASURE_UNIT,
     STATE_PER_MEASURE_UNIT,
     STATE_IDENTIFIER_UNIT,
+    STATE_UNIT_USAGE,
     STATE_CURRENCY_UNIT,
     STATE_INTEGER_WIDTH,
     STATE_NUMBERING_SYSTEM,
@@ -114,6 +117,7 @@
     STEM_MEASURE_UNIT,
     STEM_PER_MEASURE_UNIT,
     STEM_UNIT,
+    STEM_UNIT_USAGE,
     STEM_CURRENCY,
     STEM_INTEGER_WIDTH,
     STEM_NUMBERING_SYSTEM,
@@ -244,6 +248,8 @@
 
 void parseIdentifierUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
 
+void parseUnitUsageOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
+
 void parseFractionStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
 
 void generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode& status);
@@ -306,6 +312,8 @@
 
     static bool perUnit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
 
+    static bool usage(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
+
     static bool precision(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
 
     static bool roundingMode(const MacroProps& macros, UnicodeString& sb, UErrorCode& status);
@@ -334,6 +342,7 @@
     bool notation = false;
     bool unit = false;
     bool perUnit = false;
+    bool usage = false;
     bool precision = false;
     bool roundingMode = false;
     bool grouper = false;
diff --git a/icu4c/source/i18n/number_types.h b/icu4c/source/i18n/number_types.h
index 5c2b8cf..8180fe5 100644
--- a/icu4c/source/i18n/number_types.h
+++ b/icu4c/source/i18n/number_types.h
@@ -246,16 +246,17 @@
  * itself. The {@link #processQuantity} method performs the final step in the number processing pipeline: it uses the
  * quantity to generate a finalized {@link MicroProps}, which can be used to render the number to output.
  *
- * <p>
  * In other words, this interface is used for the parts of number processing that are <em>quantity-dependent</em>.
  *
- * <p>
  * In order to allow for multiple different objects to all mutate the same MicroProps, a "chain" of MicroPropsGenerators
  * are linked together, and each one is responsible for manipulating a certain quantity-dependent part of the
  * MicroProps. At the tail of the linked list is a base instance of {@link MicroProps} with properties that are not
  * quantity-dependent. Each element in the linked list calls {@link #processQuantity} on its "parent", then does its
  * work, and then returns the result.
  *
+ * This chain of MicroPropsGenerators is typically constructed by NumberFormatterImpl::macrosToMicroGenerator() when
+ * constructing a NumberFormatter.
+ *
  * Exported as U_I18N_API because it is a base class for other exported types
  *
  */
@@ -264,13 +265,12 @@
     virtual ~MicroPropsGenerator();
 
     /**
-     * Considers the given {@link DecimalQuantity}, optionally mutates it, and returns a {@link MicroProps}.
+     * Considers the given {@link DecimalQuantity}, optionally mutates it, and
+     * populates a {@link MicroProps} instance.
      *
-     * @param quantity
-     *            The quantity for consideration and optional mutation.
-     * @param micros
-     *            The MicroProps instance to populate.
-     * @return A MicroProps instance resolved for the quantity.
+     * @param quantity The quantity for consideration and optional mutation.
+     * @param micros The MicroProps instance to populate. It will be modified as
+     *   needed for the given quantity.
      */
     virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros,
                                  UErrorCode& status) const = 0;
diff --git a/icu4c/source/i18n/number_usageprefs.cpp b/icu4c/source/i18n/number_usageprefs.cpp
new file mode 100644
index 0000000..3dcc8ef
--- /dev/null
+++ b/icu4c/source/i18n/number_usageprefs.cpp
@@ -0,0 +1,124 @@
+// © 2020 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "number_usageprefs.h"
+#include "cstring.h"
+#include "number_decimalquantity.h"
+#include "number_microprops.h"
+#include "number_roundingutils.h"
+#include "unicode/char16ptr.h"
+#include "unicode/currunit.h"
+#include "unicode/fmtable.h"
+#include "unicode/measure.h"
+#include "unicode/numberformatter.h"
+#include "unicode/platform.h"
+#include "unicode/unum.h"
+#include "unicode/urename.h"
+
+using namespace icu::number;
+using namespace icu::number::impl;
+
+// Copy constructor
+Usage::Usage(const Usage &other) : fUsage(nullptr), fLength(other.fLength), fError(other.fError) {
+    if (other.fUsage != nullptr) {
+        fUsage = (char *)uprv_malloc(fLength + 1);
+        uprv_strncpy(fUsage, other.fUsage, fLength + 1);
+    }
+}
+
+// Copy assignment operator
+Usage &Usage::operator=(const Usage &other) {
+    fLength = other.fLength;
+    if (other.fUsage != nullptr) {
+        fUsage = (char *)uprv_malloc(fLength + 1);
+        uprv_strncpy(fUsage, other.fUsage, fLength + 1);
+    }
+    fError = other.fError;
+    return *this;
+}
+
+// Move constructor - can it be improved by taking over src's "this" instead of
+// copying contents? Swapping pointers makes sense for heap objects but not for
+// stack objects.
+// *this = std::move(src);
+Usage::Usage(Usage &&src) U_NOEXCEPT : fUsage(src.fUsage), fLength(src.fLength), fError(src.fError) {
+    // Take ownership away from src if necessary
+    src.fUsage = nullptr;
+}
+
+// Move assignment operator
+Usage &Usage::operator=(Usage &&src) U_NOEXCEPT {
+    if (this == &src) {
+        return *this;
+    }
+    if (fUsage != nullptr) {
+        uprv_free(fUsage);
+    }
+    fUsage = src.fUsage;
+    fLength = src.fLength;
+    fError = src.fError;
+    // Take ownership away from src if necessary
+    src.fUsage = nullptr;
+    return *this;
+}
+
+Usage::~Usage() {
+    if (fUsage != nullptr) {
+        uprv_free(fUsage);
+        fUsage = nullptr;
+    }
+}
+
+void Usage::set(StringPiece value) {
+    if (fUsage != nullptr) {
+        uprv_free(fUsage);
+        fUsage = nullptr;
+    }
+    fLength = value.length();
+    fUsage = (char *)uprv_malloc(fLength + 1);
+    uprv_strncpy(fUsage, value.data(), fLength);
+    fUsage[fLength] = 0;
+}
+
+UsagePrefsHandler::UsagePrefsHandler(const Locale &locale,
+                                     const MeasureUnit inputUnit,
+                                     const StringPiece usage,
+                                     const MicroPropsGenerator *parent,
+                                     UErrorCode &status)
+    : fUnitsRouter(inputUnit, StringPiece(locale.getCountry()), usage, status),
+      fParent(parent) {
+}
+
+void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
+                                        UErrorCode &status) const {
+    fParent->processQuantity(quantity, micros, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    quantity.roundToInfinity(); // Enables toDouble
+    auto routed = fUnitsRouter.route(quantity.toDouble(), status);
+    micros.outputUnit = routed[0]->getUnit();
+    quantity.setToDouble(routed[0]->getNumber().getDouble());
+
+    // TODO(units): here we are always overriding Precision. (1) get precision
+    // from fUnitsRouter, (2) ensure we use the UnitPreference skeleton's
+    // precision only when there isn't an explicit override we prefer to use.
+    // This needs to be handled within
+    // NumberFormatterImpl::macrosToMicroGenerator in number_formatimpl.cpp
+    Precision precision = Precision::integer().withMinDigits(2);
+    UNumberFormatRoundingMode roundingMode;
+    // Temporary until ICU 64?
+    roundingMode = precision.fRoundingMode;
+    CurrencyUnit currency(u"", status);
+    micros.rounder = {precision, roundingMode, currency, status};
+    if (U_FAILURE(status)) {
+        return;
+    }
+}
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/number_usageprefs.h b/icu4c/source/i18n/number_usageprefs.h
new file mode 100644
index 0000000..fa56c1e
--- /dev/null
+++ b/icu4c/source/i18n/number_usageprefs.h
@@ -0,0 +1,62 @@
+// © 2020 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+#ifndef __NUMBER_USAGEPREFS_H__
+#define __NUMBER_USAGEPREFS_H__
+
+#include "cmemory.h"
+#include "number_types.h"
+#include "unicode/locid.h"
+#include "unicode/measunit.h"
+#include "unicode/stringpiece.h"
+#include "unicode/uobject.h"
+#include "unitsrouter.h"
+
+U_NAMESPACE_BEGIN
+namespace number {
+namespace impl {
+
+using ::icu::units::UnitsRouter;
+
+/**
+ * A MicroPropsGenerator which uses UnitsRouter to produce output converted to a
+ * MeasureUnit appropriate for a particular localized usage: see
+ * NumberFormatterSettings::usage().
+ */
+class U_I18N_API UsagePrefsHandler : public MicroPropsGenerator, public UMemory {
+  public:
+    UsagePrefsHandler(const Locale &locale, const MeasureUnit inputUnit, const StringPiece usage,
+                      const MicroPropsGenerator *parent, UErrorCode &status);
+
+    /**
+     * Obtains the appropriate output value, MeasurementUnit and
+     * rounding/precision behaviour from the UnitsRouter.
+     */
+    void processQuantity(DecimalQuantity &quantity, MicroProps &micros,
+                         UErrorCode &status) const U_OVERRIDE;
+
+    /**
+     * Returns the list of possible output units, i.e. the full set of
+     * preferences, for the localized, usage-specific unit preferences.
+     *
+     * The returned pointer should be valid for the lifetime of the
+     * UsagePrefsHandler instance.
+     */
+    const MaybeStackVector<MeasureUnit> *getOutputUnits() const {
+        return fUnitsRouter.getOutputUnits();
+    }
+
+  private:
+    UnitsRouter fUnitsRouter;
+    const MicroPropsGenerator *fParent;
+};
+
+} // namespace impl
+} // namespace number
+U_NAMESPACE_END
+
+#endif // __NUMBER_USAGEPREFS_H__
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/number_utypes.h b/icu4c/source/i18n/number_utypes.h
index 7a1b7a4..d97eadc 100644
--- a/icu4c/source/i18n/number_utypes.h
+++ b/icu4c/source/i18n/number_utypes.h
@@ -28,9 +28,6 @@
  * This struct is held internally by the C++ version FormattedNumber since the member types are not
  * declared in the public header file.
  *
- * The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used
- * to add a toDecNumber() or similar method.
- *
  * Exported as U_I18N_API for tests
  */
 class U_I18N_API UFormattedNumberData : public FormattedValueStringBuilderImpl {
@@ -38,7 +35,13 @@
     UFormattedNumberData() : FormattedValueStringBuilderImpl(kUndefinedField) {}
     virtual ~UFormattedNumberData();
 
+    // The formatted quantity.
     DecimalQuantity quantity;
+
+    // The output unit for the formatted quantity.
+    // TODO(units,hugovdm): populate this correctly for the general case - it's
+    // currently only implemented for the .usage() use case.
+    MeasureUnit outputUnit;
 };
 
 
diff --git a/icu4c/source/i18n/sources.txt b/icu4c/source/i18n/sources.txt
index ce2486b..8c989ed 100644
--- a/icu4c/source/i18n/sources.txt
+++ b/icu4c/source/i18n/sources.txt
@@ -122,6 +122,7 @@
 number_rounding.cpp
 number_scientific.cpp
 number_skeletons.cpp
+number_usageprefs.cpp
 number_utils.cpp
 numfmt.cpp
 numparse_affixes.cpp
diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h
index 0fb16c2..bc4326f 100644
--- a/icu4c/source/i18n/unicode/numberformatter.h
+++ b/icu4c/source/i18n/unicode/numberformatter.h
@@ -158,6 +158,7 @@
 class MutablePatternModifier;
 class ImmutablePatternModifier;
 struct DecimalFormatWarehouse;
+class UsagePrefsHandler;
 
 /**
  * Used for NumberRangeFormatter and implemented in numrange_fluent.cpp.
@@ -767,6 +768,11 @@
 
     // To allow access to the skeleton generation code:
     friend class impl::GeneratorHelpers;
+
+    // TODO(units): revisit when UnitsRouter is changed: do we still need this
+    // once Precision is returned by UnitsRouter? For now, we allow access to
+    // Precision constructor from UsagePrefsHandler:
+    friend class impl::UsagePrefsHandler;
 };
 
 /**
@@ -1127,6 +1133,61 @@
 
 namespace impl {
 
+// Do not enclose entire Usage with #ifndef U_HIDE_INTERNAL_API, needed for a protected field
+/**
+ * Manages NumberFormatterSettings::usage()'s char* instance on the heap.
+ * @internal
+ */
+class U_I18N_API Usage : public UMemory {
+
+#ifndef U_HIDE_INTERNAL_API
+
+  public:
+    /** @internal */
+    Usage(const Usage& other);
+
+    /** @internal */
+    Usage& operator=(const Usage& other);
+
+    /** @internal */
+    Usage(Usage &&src) U_NOEXCEPT;
+
+    /** @internal */
+    Usage& operator=(Usage&& src) U_NOEXCEPT;
+
+    /** @internal */
+    ~Usage();
+
+    /** @internal */
+    int16_t length() const { return fLength; }
+
+    /** @internal
+     * Makes a copy of value.
+     */
+    void set(StringPiece value);
+
+    /** @internal */
+    bool isSet() const { return fLength > 0; }
+
+  private:
+    char *fUsage;
+    int16_t fLength;
+    UErrorCode fError;
+
+    Usage() : fUsage(nullptr), fLength(0), fError(U_ZERO_ERROR) {}
+
+    // Allow NumberFormatterImpl to access fUsage.
+    friend class impl::NumberFormatterImpl;
+
+    // Allow skeleton generation code to access private members.
+    friend class impl::GeneratorHelpers;
+
+    // Allow MacroProps/MicroProps to initialize empty instances.
+    friend struct impl::MacroProps;
+
+#endif // U_HIDE_INTERNAL_API
+};
+
 // Do not enclose entire SymbolsWrapper with #ifndef U_HIDE_INTERNAL_API, needed for a protected field
 /** @internal */
 class U_I18N_API SymbolsWrapper : public UMemory {
@@ -1411,6 +1472,9 @@
     Scale scale;  // = Scale();  (benign value)
 
     /** @internal */
+    Usage usage;  // = Usage();  (no usage)
+
+    /** @internal */
     const AffixPatternProvider* affixProvider = nullptr;  // no ownership
 
     /** @internal */
@@ -2073,6 +2137,13 @@
      * Setting usage to an empty string clears the usage (disables usage-based
      * localized formatting).
      *
+     * Setting a usage string but not a correct input unit will result in an
+     * U_ILLEGAL_ARGUMENT_ERROR.
+     *
+     * When using usage, specifying rounding or precision is unnecessary.
+     * Specifying a precision in some manner will override the default
+     * formatting.
+     *
      * @param usage A `usage` parameter from the units resource. See the
      * unitPreferenceData in *source/data/misc/units.txt*, generated from
      * `unitPreferenceData` in [CLDR's
@@ -2575,7 +2646,7 @@
      * The output unit is dependent upon the localized preferences for the usage
      * specified via NumberFormatterSettings::usage(), and may be a unit with
      * UMEASURE_UNIT_MIXED unit complexity (MeasureUnit::getComplexity()), such
-     * as "foot+inch" or "hour+minute+second".
+     * as "foot-and-inch" or "hour-and-minute-and-second".
      *
      * @return `MeasureUnit`.
      * @draft ICU 68
diff --git a/icu4c/source/i18n/unitconverter.cpp b/icu4c/source/i18n/unitconverter.cpp
index 70adbd7..97a5e3d 100644
--- a/icu4c/source/i18n/unitconverter.cpp
+++ b/icu4c/source/i18n/unitconverter.cpp
@@ -5,146 +5,124 @@
 
 #if !UCONFIG_NO_FORMATTING
 
-#include <cmath>
-
 #include "charstr.h"
-#include "double-conversion.h"
+#include "cmemory.h"
+#include "double-conversion-string-to-double.h"
 #include "measunit_impl.h"
-#include "unicode/errorcode.h"
+#include "uassert.h"
+#include "unicode/localpointer.h"
 #include "unicode/measunit.h"
 #include "unicode/stringpiece.h"
 #include "unitconverter.h"
+#include <algorithm>
+#include <cmath>
+#include <stdlib.h>
+#include <utility>
 
 U_NAMESPACE_BEGIN
+namespace units {
+
+void U_I18N_API Factor::multiplyBy(const Factor &rhs) {
+    factorNum *= rhs.factorNum;
+    factorDen *= rhs.factorDen;
+    for (int i = 0; i < CONSTANTS_COUNT; i++) {
+        constants[i] += rhs.constants[i];
+    }
+
+    // NOTE
+    //  We need the offset when the source and the target are simple units. e.g. the source is
+    //  celsius and the target is Fahrenheit. Therefore, we just keep the value using `std::max`.
+    offset = std::max(rhs.offset, offset);
+}
+
+void U_I18N_API Factor::divideBy(const Factor &rhs) {
+    factorNum *= rhs.factorDen;
+    factorDen *= rhs.factorNum;
+    for (int i = 0; i < CONSTANTS_COUNT; i++) {
+        constants[i] -= rhs.constants[i];
+    }
+
+    // NOTE
+    //  We need the offset when the source and the target are simple units. e.g. the source is
+    //  celsius and the target is Fahrenheit. Therefore, we just keep the value using `std::max`.
+    offset = std::max(rhs.offset, offset);
+}
+
+void U_I18N_API Factor::power(int32_t power) {
+    // multiply all the constant by the power.
+    for (int i = 0; i < CONSTANTS_COUNT; i++) {
+        constants[i] *= power;
+    }
+
+    bool shouldFlip = power < 0; // This means that after applying the absolute power, we should flip
+                                 // the Numerator and Denominator.
+
+    factorNum = std::pow(factorNum, std::abs(power));
+    factorDen = std::pow(factorDen, std::abs(power));
+
+    if (shouldFlip) {
+        // Flip Numerator and Denominator.
+        std::swap(factorNum, factorDen);
+    }
+}
+
+void U_I18N_API Factor::flip() {
+    std::swap(factorNum, factorDen);
+
+    for (int i = 0; i < CONSTANTS_COUNT; i++) {
+        constants[i] *= -1;
+    }
+}
+
+void U_I18N_API Factor::applySiPrefix(UMeasureSIPrefix siPrefix) {
+    if (siPrefix == UMeasureSIPrefix::UMEASURE_SI_PREFIX_ONE) return; // No need to do anything
+
+    double siApplied = std::pow(10.0, std::abs(siPrefix));
+
+    if (siPrefix < 0) {
+        factorDen *= siApplied;
+        return;
+    }
+
+    factorNum *= siApplied;
+}
+
+void U_I18N_API Factor::substituteConstants() {
+    // These values are a hard-coded subset of unitConstants in the units
+    // resources file. A unit test checks that all constants in the resource
+    // file are at least recognised by the code. Derived constants' values or
+    // hard-coded derivations are not checked.
+    // double constantsValues[CONSTANTS_COUNT];
+    static const double constantsValues[CONSTANTS_COUNT] = {
+        [CONSTANT_FT2M] = 0.3048,                  //
+        [CONSTANT_PI] = 411557987.0 / 131002976.0, //
+        [CONSTANT_GRAVITY] = 9.80665,              //
+        [CONSTANT_G] = 6.67408E-11,                //
+        [CONSTANT_GAL_IMP2M3] = 0.00454609,        //
+        [CONSTANT_LB2KG] = 0.45359237,             //
+    };
+
+    for (int i = 0; i < CONSTANTS_COUNT; i++) {
+        if (this->constants[i] == 0) {
+            continue;
+        }
+
+        auto absPower = std::abs(this->constants[i]);
+        Signum powerSig = this->constants[i] < 0 ? Signum::NEGATIVE : Signum::POSITIVE;
+        double absConstantValue = std::pow(constantsValues[i], absPower);
+
+        if (powerSig == Signum::NEGATIVE) {
+            this->factorDen *= absConstantValue;
+        } else {
+            this->factorNum *= absConstantValue;
+        }
+
+        this->constants[i] = 0;
+    }
+}
 
 namespace {
 
-/* Internal Structure */
-
-enum Constants {
-    CONSTANT_FT2M,    // ft2m stands for foot to meter.
-    CONSTANT_PI,      // PI
-    CONSTANT_GRAVITY, // Gravity
-    CONSTANT_G,
-    CONSTANT_GAL_IMP2M3, // Gallon imp to m3
-    CONSTANT_LB2KG,      // Pound to Kilogram
-
-    // Must be the last element.
-    CONSTANTS_COUNT
-};
-
-typedef enum SigNum {
-    NEGATIVE = -1,
-    POSITIVE = 1,
-} SigNum;
-
-/* Represents a conversion factor */
-struct Factor {
-    double factorNum = 1;
-    double factorDen = 1;
-    double offset = 0;
-    bool reciprocal = false;
-    int32_t constants[CONSTANTS_COUNT] = {};
-
-    void multiplyBy(const Factor &rhs) {
-        factorNum *= rhs.factorNum;
-        factorDen *= rhs.factorDen;
-        for (int i = 0; i < CONSTANTS_COUNT; i++) {
-            constants[i] += rhs.constants[i];
-        }
-
-        // NOTE
-        //  We need the offset when the source and the target are simple units. e.g. the source is
-        //  celsius and the target is Fahrenheit. Therefore, we just keep the value using `std::max`.
-        offset = std::max(rhs.offset, offset);
-    }
-
-    void divideBy(const Factor &rhs) {
-        factorNum *= rhs.factorDen;
-        factorDen *= rhs.factorNum;
-        for (int i = 0; i < CONSTANTS_COUNT; i++) {
-            constants[i] -= rhs.constants[i];
-        }
-
-        // NOTE
-        //  We need the offset when the source and the target are simple units. e.g. the source is
-        //  celsius and the target is Fahrenheit. Therefore, we just keep the value using `std::max`.
-        offset = std::max(rhs.offset, offset);
-    }
-
-    // Apply the power to the factor.
-    void power(int32_t power) {
-        // multiply all the constant by the power.
-        for (int i = 0; i < CONSTANTS_COUNT; i++) {
-            constants[i] *= power;
-        }
-
-        bool shouldFlip = power < 0; // This means that after applying the absolute power, we should flip
-                                     // the Numerator and Denominator.
-
-        factorNum = std::pow(factorNum, std::abs(power));
-        factorDen = std::pow(factorDen, std::abs(power));
-
-        if (shouldFlip) {
-            // Flip Numerator and Denominator.
-            std::swap(factorNum, factorDen);
-        }
-    }
-
-    // Flip the `Factor`, for example, factor= 2/3, flippedFactor = 3/2
-    void flip() {
-        std::swap(factorNum, factorDen);
-
-        for (int i = 0; i < CONSTANTS_COUNT; i++) {
-            constants[i] *= -1;
-        }
-    }
-
-    // Apply SI prefix to the `Factor`
-    void applySiPrefix(UMeasureSIPrefix siPrefix) {
-        if (siPrefix == UMeasureSIPrefix::UMEASURE_SI_PREFIX_ONE) return; // No need to do anything
-
-        double siApplied = std::pow(10.0, std::abs(siPrefix));
-
-        if (siPrefix < 0) {
-            factorDen *= siApplied;
-            return;
-        }
-
-        factorNum *= siApplied;
-    }
-
-    void substituteConstants() {
-        double constantsValues[CONSTANTS_COUNT];
-
-        // TODO: Load those constant values from units data.
-        constantsValues[CONSTANT_FT2M] = 0.3048;
-        constantsValues[CONSTANT_PI] = 411557987.0 / 131002976.0;
-        constantsValues[CONSTANT_GRAVITY] = 9.80665;
-        constantsValues[CONSTANT_G] = 6.67408E-11;
-        constantsValues[CONSTANT_LB2KG] = 0.45359237;
-        constantsValues[CONSTANT_GAL_IMP2M3] = 0.00454609;
-
-        for (int i = 0; i < CONSTANTS_COUNT; i++) {
-            if (this->constants[i] == 0) {
-                continue;
-            }
-
-            auto absPower = std::abs(this->constants[i]);
-            SigNum powerSig = this->constants[i] < 0 ? SigNum::NEGATIVE : SigNum::POSITIVE;
-            double absConstantValue = std::pow(constantsValues[i], absPower);
-
-            if (powerSig == SigNum::NEGATIVE) {
-                this->factorDen *= absConstantValue;
-            } else {
-                this->factorNum *= absConstantValue;
-            }
-
-            this->constants[i] = 0;
-        }
-    }
-};
-
 /* Helpers */
 
 using icu::double_conversion::StringToDoubleConverter;
@@ -184,48 +162,10 @@
 }
 
 /*
- * Adds a single factor element to the `Factor`. e.g "ft3m", "2.333" or "cup2m3". But not "cup2m3^3".
- */
-void addSingleFactorConstant(StringPiece baseStr, int32_t power, SigNum sigNum, Factor &factor,
-                             UErrorCode &status) {
-
-    if (baseStr == "ft_to_m") {
-        factor.constants[CONSTANT_FT2M] += power * sigNum;
-    } else if (baseStr == "ft2_to_m2") {
-        factor.constants[CONSTANT_FT2M] += 2 * power * sigNum;
-    } else if (baseStr == "ft3_to_m3") {
-        factor.constants[CONSTANT_FT2M] += 3 * power * sigNum;
-    } else if (baseStr == "in3_to_m3") {
-        factor.constants[CONSTANT_FT2M] += 3 * power * sigNum;
-        factor.factorDen *= 12 * 12 * 12;
-    } else if (baseStr == "gal_to_m3") {
-        factor.factorNum *= 231;
-        factor.constants[CONSTANT_FT2M] += 3 * power * sigNum;
-        factor.factorDen *= 12 * 12 * 12;
-    } else if (baseStr == "gal_imp_to_m3") {
-        factor.constants[CONSTANT_GAL_IMP2M3] += power * sigNum;
-    } else if (baseStr == "G") {
-        factor.constants[CONSTANT_G] += power * sigNum;
-    } else if (baseStr == "gravity") {
-        factor.constants[CONSTANT_GRAVITY] += power * sigNum;
-    } else if (baseStr == "lb_to_kg") {
-        factor.constants[CONSTANT_LB2KG] += power * sigNum;
-    } else if (baseStr == "PI") {
-        factor.constants[CONSTANT_PI] += power * sigNum;
-    } else {
-        if (sigNum == SigNum::NEGATIVE) {
-            factor.factorDen *= std::pow(strToDouble(baseStr, status), power);
-        } else {
-            factor.factorNum *= std::pow(strToDouble(baseStr, status), power);
-        }
-    }
-}
-
-/*
   Adds single factor to a `Factor` object. Single factor means "23^2", "23.3333", "ft2m^3" ...etc.
   However, complex factor are not included, such as "ft2m^3*200/3"
 */
-void addFactorElement(Factor &factor, StringPiece elementStr, SigNum sigNum, UErrorCode &status) {
+void addFactorElement(Factor &factor, StringPiece elementStr, Signum signum, UErrorCode &status) {
     StringPiece baseStr;
     StringPiece powerStr;
     int32_t power =
@@ -250,7 +190,7 @@
         baseStr = elementStr;
     }
 
-    addSingleFactorConstant(baseStr, power, sigNum, factor, status);
+    addSingleFactorConstant(baseStr, power, signum, factor, status);
 }
 
 /*
@@ -258,21 +198,21 @@
  */
 Factor extractFactorConversions(StringPiece stringFactor, UErrorCode &status) {
     Factor result;
-    SigNum sigNum = SigNum::POSITIVE;
+    Signum signum = Signum::POSITIVE;
     auto factorData = stringFactor.data();
     for (int32_t i = 0, start = 0, n = stringFactor.length(); i < n; i++) {
         if (factorData[i] == '*' || factorData[i] == '/') {
             StringPiece factorElement = stringFactor.substr(start, i - start);
-            addFactorElement(result, factorElement, sigNum, status);
+            addFactorElement(result, factorElement, signum, status);
 
             start = i + 1; // Set `start` to point to the start of the new element.
         } else if (i == n - 1) {
             // Last element
-            addFactorElement(result, stringFactor.substr(start, i + 1), sigNum, status);
+            addFactorElement(result, stringFactor.substr(start, i + 1), signum, status);
         }
 
         if (factorData[i] == '/') {
-            sigNum = SigNum::NEGATIVE; // Change the sigNum because we reached the Denominator.
+            signum = Signum::NEGATIVE; // Change the signum because we reached the Denominator.
         }
     }
 
@@ -386,6 +326,44 @@
 
 } // namespace
 
+// Conceptually, this modifies factor: factor *= baseStr^(signum*power).
+//
+// baseStr must be a known constant or a value that strToDouble() is able to
+// parse.
+void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum signum,
+                                        Factor &factor, UErrorCode &status) {
+    if (baseStr == "ft_to_m") {
+        factor.constants[CONSTANT_FT2M] += power * signum;
+    } else if (baseStr == "ft2_to_m2") {
+        factor.constants[CONSTANT_FT2M] += 2 * power * signum;
+    } else if (baseStr == "ft3_to_m3") {
+        factor.constants[CONSTANT_FT2M] += 3 * power * signum;
+    } else if (baseStr == "in3_to_m3") {
+        factor.constants[CONSTANT_FT2M] += 3 * power * signum;
+        factor.factorDen *= 12 * 12 * 12;
+    } else if (baseStr == "gal_to_m3") {
+        factor.factorNum *= 231;
+        factor.constants[CONSTANT_FT2M] += 3 * power * signum;
+        factor.factorDen *= 12 * 12 * 12;
+    } else if (baseStr == "gal_imp_to_m3") {
+        factor.constants[CONSTANT_GAL_IMP2M3] += power * signum;
+    } else if (baseStr == "G") {
+        factor.constants[CONSTANT_G] += power * signum;
+    } else if (baseStr == "gravity") {
+        factor.constants[CONSTANT_GRAVITY] += power * signum;
+    } else if (baseStr == "lb_to_kg") {
+        factor.constants[CONSTANT_LB2KG] += power * signum;
+    } else if (baseStr == "PI") {
+        factor.constants[CONSTANT_PI] += power * signum;
+    } else {
+        if (signum == Signum::NEGATIVE) {
+            factor.factorDen *= std::pow(strToDouble(baseStr, status), power);
+        } else {
+            factor.factorNum *= std::pow(strToDouble(baseStr, status), power);
+        }
+    }
+}
+
 /**
  * Extracts the compound base unit of a compound unit (`source`). For example, if the source unit is
  * `square-mile-per-hour`, the compound base unit will be `square-meter-per-second`
@@ -414,7 +392,7 @@
         }
 
         // Multiply the power of the singleUnit by the power of the baseUnit. For example, square-hectare
-        // must be p4-meter. (NOTE: hectare --> square-meter)
+        // must be pow4-meter. (NOTE: hectare --> square-meter)
         auto compoundBaseUnit = MeasureUnit::forIdentifier(rateInfo->baseUnit.toStringPiece(), status);
 
         int32_t baseUnitsCount;
@@ -503,6 +481,7 @@
     return result * 1.000000000001;
 }
 
+} // namespace units
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/unitconverter.h b/icu4c/source/i18n/unitconverter.h
index 3b29a66..d19994b 100644
--- a/icu4c/source/i18n/unitconverter.h
+++ b/icu4c/source/i18n/unitconverter.h
@@ -7,13 +7,60 @@
 #ifndef __UNITCONVERTER_H__
 #define __UNITCONVERTER_H__
 
-#include "cmemory.h"
-#include "unicode/errorcode.h"
 #include "unicode/measunit.h"
-#include "unitconverter.h"
+#include "unicode/stringpiece.h"
+#include "unicode/uobject.h"
 #include "unitsdata.h"
 
 U_NAMESPACE_BEGIN
+namespace units {
+
+/* Internal Structure */
+
+enum Constants {
+    CONSTANT_FT2M,    // ft2m stands for foot to meter.
+    CONSTANT_PI,      // PI
+    CONSTANT_GRAVITY, // Gravity
+    CONSTANT_G,
+    CONSTANT_GAL_IMP2M3, // Gallon imp to m3
+    CONSTANT_LB2KG,      // Pound to Kilogram
+
+    // Must be the last element.
+    CONSTANTS_COUNT
+};
+
+typedef enum Signum {
+    NEGATIVE = -1,
+    POSITIVE = 1,
+} Signum;
+
+/* Represents a conversion factor */
+struct U_I18N_API Factor {
+    double factorNum = 1;
+    double factorDen = 1;
+    double offset = 0;
+    bool reciprocal = false;
+    int32_t constants[CONSTANTS_COUNT] = {};
+
+    void multiplyBy(const Factor &rhs);
+    void divideBy(const Factor &rhs);
+
+    // Apply the power to the factor.
+    void power(int32_t power);
+
+    // Flip the `Factor`, for example, factor= 2/3, flippedFactor = 3/2
+    void flip();
+
+    // Apply SI prefix to the `Factor`
+    void applySiPrefix(UMeasureSIPrefix siPrefix);
+    void substituteConstants();
+};
+
+/*
+ * Adds a single factor element to the `Factor`. e.g "ft3m", "2.333" or "cup2m3". But not "cup2m3^3".
+ */
+void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum sigNum,
+                                        Factor &factor, UErrorCode &status);
 
 /**
  * Represents the conversion rate between `source` and `target`.
@@ -90,6 +137,7 @@
     ConversionRate conversionRate_;
 };
 
+} // namespace units
 U_NAMESPACE_END
 
 #endif //__UNITCONVERTER_H__
diff --git a/icu4c/source/i18n/unitsdata.cpp b/icu4c/source/i18n/unitsdata.cpp
index 1ac285f..7b7f7df 100644
--- a/icu4c/source/i18n/unitsdata.cpp
+++ b/icu4c/source/i18n/unitsdata.cpp
@@ -5,15 +5,19 @@
 
 #if !UCONFIG_NO_FORMATTING
 
-#include "number_decimalquantity.h"
 #include "cstring.h"
 #include "number_decimalquantity.h"
 #include "resource.h"
+#include "uassert.h"
+#include "unicode/unistr.h"
+#include "unicode/ures.h"
 #include "unitsdata.h"
 #include "uresimp.h"
 #include "util.h"
+#include <utility>
 
 U_NAMESPACE_BEGIN
+namespace units {
 
 namespace {
 
@@ -108,40 +112,6 @@
     MaybeStackVector<ConversionRateInfo> *outVector;
 };
 
-UnitPreferenceMetadata::UnitPreferenceMetadata(StringPiece category, StringPiece usage,
-                                               StringPiece region, int32_t prefsOffset,
-                                               int32_t prefsCount, UErrorCode &status) {
-    this->category.append(category, status);
-    this->usage.append(usage, status);
-    this->region.append(region, status);
-    this->prefsOffset = prefsOffset;
-    this->prefsCount = prefsCount;
-}
-
-int32_t UnitPreferenceMetadata::compareTo(const UnitPreferenceMetadata &other) const {
-    int32_t cmp = uprv_strcmp(category.data(), other.category.data());
-    if (cmp == 0) { cmp = uprv_strcmp(usage.data(), other.usage.data()); }
-    if (cmp == 0) { cmp = uprv_strcmp(region.data(), other.region.data()); }
-    return cmp;
-}
-
-int32_t UnitPreferenceMetadata::compareTo(const UnitPreferenceMetadata &other, bool *foundCategory,
-                                          bool *foundUsage, bool *foundRegion) const {
-    int32_t cmp = uprv_strcmp(category.data(), other.category.data());
-    if (cmp == 0) {
-        *foundCategory = true;
-        cmp = uprv_strcmp(usage.data(), other.usage.data());
-    }
-    if (cmp == 0) {
-        *foundUsage = true;
-        cmp = uprv_strcmp(region.data(), other.region.data());
-    }
-    if (cmp == 0) {
-        *foundRegion = true;
-    }
-    return cmp;
-}
-
 bool operator<(const UnitPreferenceMetadata &a, const UnitPreferenceMetadata &b) {
     return a.compareTo(b) < 0;
 }
@@ -325,6 +295,9 @@
         } else if (uprv_strcmp(desired.usage.data(), "default") != 0) {
             desired.usage.truncate(0).append("default", status);
         } else {
+            // TODO(icu-units/icu#36): reconsider consistency of errors.
+            // Currently this U_MISSING_RESOURCE_ERROR propagates when a
+            // U_NUMBER_SKELETON_SYNTAX_ERROR might be much more intuitive.
             status = U_MISSING_RESOURCE_ERROR;
             return -1;
         }
@@ -352,6 +325,44 @@
 
 } // namespace
 
+UnitPreferenceMetadata::UnitPreferenceMetadata(StringPiece category, StringPiece usage,
+                                               StringPiece region, int32_t prefsOffset,
+                                               int32_t prefsCount, UErrorCode &status) {
+    this->category.append(category, status);
+    this->usage.append(usage, status);
+    this->region.append(region, status);
+    this->prefsOffset = prefsOffset;
+    this->prefsCount = prefsCount;
+}
+
+int32_t UnitPreferenceMetadata::compareTo(const UnitPreferenceMetadata &other) const {
+    int32_t cmp = uprv_strcmp(category.data(), other.category.data());
+    if (cmp == 0) {
+        cmp = uprv_strcmp(usage.data(), other.usage.data());
+    }
+    if (cmp == 0) {
+        cmp = uprv_strcmp(region.data(), other.region.data());
+    }
+    return cmp;
+}
+
+int32_t UnitPreferenceMetadata::compareTo(const UnitPreferenceMetadata &other, bool *foundCategory,
+                                          bool *foundUsage, bool *foundRegion) const {
+    int32_t cmp = uprv_strcmp(category.data(), other.category.data());
+    if (cmp == 0) {
+        *foundCategory = true;
+        cmp = uprv_strcmp(usage.data(), other.usage.data());
+    }
+    if (cmp == 0) {
+        *foundUsage = true;
+        cmp = uprv_strcmp(region.data(), other.region.data());
+    }
+    if (cmp == 0) {
+        *foundRegion = true;
+    }
+    return cmp;
+}
+
 CharString U_I18N_API getUnitCategory(const char *baseUnitIdentifier, UErrorCode &status) {
     CharString result;
     LocalUResourceBundlePointer unitsBundle(ures_openDirect(NULL, "units", &status));
@@ -414,6 +425,7 @@
     preferenceCount = m->prefsCount;
 }
 
+} // namespace units
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/unitsdata.h b/icu4c/source/i18n/unitsdata.h
index 1ea8dee..6f1c1ec 100644
--- a/icu4c/source/i18n/unitsdata.h
+++ b/icu4c/source/i18n/unitsdata.h
@@ -9,10 +9,11 @@
 
 #include "charstr.h"
 #include "cmemory.h"
-#include "unicode/measunit.h"
 #include "unicode/stringpiece.h"
+#include "unicode/uobject.h"
 
 U_NAMESPACE_BEGIN
+namespace units {
 
 /**
  * Looks up the unit category of a base unit identifier.
@@ -97,8 +98,6 @@
     CharString skeleton;
 };
 
-namespace {
-
 /**
  * Metadata about the preferences in UnitPreferences::unitPrefs_.
  *
@@ -134,8 +133,6 @@
                       bool *foundRegion) const;
 };
 
-} // namespace
-
 /**
  * Unit Preferences information for various locales and usages.
  */
@@ -186,6 +183,7 @@
     MaybeStackVector<UnitPreference> unitPrefs_;
 };
 
+} // namespace units
 U_NAMESPACE_END
 
 #endif //__GETUNITSDATA_H__
diff --git a/icu4c/source/i18n/unitsrouter.cpp b/icu4c/source/i18n/unitsrouter.cpp
index 9f787ce..5ca7b62 100644
--- a/icu4c/source/i18n/unitsrouter.cpp
+++ b/icu4c/source/i18n/unitsrouter.cpp
@@ -5,19 +5,15 @@
 
 #if !UCONFIG_NO_FORMATTING
 
-#include <stdio.h>
-#include <utility>
-
+#include "charstr.h"
 #include "cmemory.h"
-#include "cstring.h"
-#include "number_decimalquantity.h"
-#include "resource.h"
-#include "unitconverter.h" // for extractCompoundBaseUnit
-#include "unitsdata.h"     // for getUnitCategory
+#include "unicode/measure.h"
+#include "unitconverter.h"
+#include "unitsdata.h"
 #include "unitsrouter.h"
-#include "uresimp.h"
 
 U_NAMESPACE_BEGIN
+namespace units {
 
 UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece usage,
                          UErrorCode &status) {
@@ -41,6 +37,7 @@
             return;
         }
 
+        outputUnits_.emplaceBackAndCheckErrorCode(status, complexTargetUnit);
         converterPreferences_.emplaceBackAndCheckErrorCode(status, inputUnit, complexTargetUnit,
                                                            preference.geq, conversionRates, status);
         if (U_FAILURE(status)) {
@@ -49,7 +46,7 @@
     }
 }
 
-MaybeStackVector<Measure> UnitsRouter::route(double quantity, UErrorCode &status) {
+MaybeStackVector<Measure> UnitsRouter::route(double quantity, UErrorCode &status) const {
     for (int i = 0, n = converterPreferences_.length(); i < n; i++) {
         const auto &converterPreference = *converterPreferences_[i];
 
@@ -63,6 +60,11 @@
     return lastConverter.convert(quantity, status);
 }
 
+const MaybeStackVector<MeasureUnit> *UnitsRouter::getOutputUnits() const {
+    return &outputUnits_;
+}
+
+} // namespace units
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/unitsrouter.h b/icu4c/source/i18n/unitsrouter.h
index 1b29d9d..c8b8a64 100644
--- a/icu4c/source/i18n/unitsrouter.h
+++ b/icu4c/source/i18n/unitsrouter.h
@@ -9,17 +9,20 @@
 
 #include <limits>
 
-#include "charstr.h" // CharString
 #include "cmemory.h"
 #include "complexunitsconverter.h"
-#include "unicode/errorcode.h"
 #include "unicode/measunit.h"
-#include "unicode/measure.h"
 #include "unicode/stringpiece.h"
+#include "unicode/uobject.h"
 #include "unitsdata.h"
 
 U_NAMESPACE_BEGIN
 
+// Forward declarations
+class Measure;
+
+namespace units {
+
 /**
  * Contains the complex unit converter and the limit which representing the smallest value that the
  * converter should accept. For example, if the converter is converting to `foot+inch` and the limit
@@ -75,12 +78,25 @@
   public:
     UnitsRouter(MeasureUnit inputUnit, StringPiece locale, StringPiece usage, UErrorCode &status);
 
-    MaybeStackVector<Measure> route(double quantity, UErrorCode &status);
+    MaybeStackVector<Measure> route(double quantity, UErrorCode &status) const;
+
+    /**
+     * Returns the list of possible output units, i.e. the full set of
+     * preferences, for the localized, usage-specific unit preferences.
+     *
+     * The returned pointer should be valid for the lifetime of the
+     * UnitsRouter instance.
+     */
+    const MaybeStackVector<MeasureUnit> *getOutputUnits() const;
 
   private:
+    // List of possible output units
+    MaybeStackVector<MeasureUnit> outputUnits_;
+
     MaybeStackVector<ConverterPreference> converterPreferences_;
 };
 
+} // namespace units
 U_NAMESPACE_END
 
 #endif //__UNITSROUTER_H__
diff --git a/icu4c/source/test/depstest/dependencies.txt b/icu4c/source/test/depstest/dependencies.txt
index 480f07f..b35fa45 100644
--- a/icu4c/source/test/depstest/dependencies.txt
+++ b/icu4c/source/test/depstest/dependencies.txt
@@ -979,6 +979,8 @@
     number_representation format formatted_value_sbimpl
     # PluralRules internals:
     unifiedcache
+    # Unit Formatting
+    units
 
 group: numberformatter
     # ICU 60+ NumberFormatter API
@@ -990,11 +992,11 @@
     number_mapper.o number_modifiers.o number_multiplier.o
     number_notation.o number_padding.o
     number_patternmodifier.o number_patternstring.o number_rounding.o
-    number_scientific.o
+    number_scientific.o number_usageprefs.o
     currpinf.o dcfmtsym.o numsys.o
     numrange_fluent.o numrange_impl.o
   deps
-    decnumber double_conversion formattable units
+    decnumber double_conversion formattable units unitsformatter
     number_representation number_output
     uclean_i18n common
 
diff --git a/icu4c/source/test/intltest/itformat.h b/icu4c/source/test/intltest/itformat.h
index d8b1799..feb55e2 100644
--- a/icu4c/source/test/intltest/itformat.h
+++ b/icu4c/source/test/intltest/itformat.h
@@ -35,6 +35,10 @@
 
 class IntlTestWithFieldPosition : public IntlTest {
 public:
+    // Tests FormattedValue's toString, toTempString, and nextPosition methods.
+    //
+    // expectedCategory gets combined with expectedFieldPositions to call
+    // checkMixedFormattedValue.
     void checkFormattedValue(
         const char16_t* message,
         const FormattedValue& fv,
@@ -43,6 +47,7 @@
         const UFieldPosition* expectedFieldPositions,
         int32_t length);
 
+    // Tests FormattedValue's toString, toTempString, and nextPosition methods.
     void checkMixedFormattedValue(
         const char16_t* message,
         const FormattedValue& fv,
diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h
index 668885d..c96c329 100644
--- a/icu4c/source/test/intltest/numbertest.h
+++ b/icu4c/source/test/intltest/numbertest.h
@@ -54,6 +54,7 @@
     void notationCompact();
     void unitMeasure();
     void unitCompoundMeasure();
+    void unitUsage();
     void unitCurrency();
     void unitPercent();
     void percentParity();
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index 88cb1da..cb30123 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -75,6 +75,7 @@
         TESTCASE_AUTO(notationCompact);
         TESTCASE_AUTO(unitMeasure);
         TESTCASE_AUTO(unitCompoundMeasure);
+        TESTCASE_AUTO(unitUsage);
         TESTCASE_AUTO(unitCurrency);
         TESTCASE_AUTO(unitPercent);
         if (!quick) {
@@ -687,6 +688,79 @@
             u"5 a\u00F1os");
 }
 
+void NumberFormatterApiTest::unitUsage() {
+    UnlocalizedNumberFormatter unloc_formatter =
+        NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
+
+    IcuTestErrorCode status(*this, "unitUsage()");
+
+    LocalizedNumberFormatter formatter = unloc_formatter.locale("en-ZA");
+    FormattedNumber formattedNum = formatter.formatDouble(300, status);
+    assertTrue(UnicodeString("unitUsage() en-ZA road, got outputUnit: \"") +
+                   formattedNum.getOutputUnit(status).getIdentifier() + "\"",
+               MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
+    assertEquals("unitUsage() en-ZA road", "300 m", formattedNum.toString(status));
+    assertFormatDescendingBig(
+            u"unitUsage() en-ZA road",
+            u"measure-unit/length-meter usage/road",
+            u"unit/meter usage/road",
+            unloc_formatter,
+            Locale("en-ZA"),
+            u"87\u00A0650 km",
+            u"8\u00A0765 km",
+            u"877 km",
+            u"88 km",
+            u"8,8 km",
+            u"877 m",
+            u"88 m",
+            u"8,8 m",
+            u"0 m");
+
+    formatter = unloc_formatter.locale("en-GB");
+    formattedNum = formatter.formatDouble(300, status);
+    assertTrue(UnicodeString("unitUsage() en-GB road, got outputUnit: \"") +
+                   formattedNum.getOutputUnit(status).getIdentifier() + "\"",
+               MeasureUnit::getYard() == formattedNum.getOutputUnit(status));
+    assertEquals("unitUsage() en-GB road", "328 yd", formattedNum.toString(status));
+    assertFormatDescendingBig(
+            u"unitUsage() en-GB road",
+            u"measure-unit/length-meter usage/road",
+            u"unit/meter usage/road",
+            unloc_formatter,
+            Locale("en-GB"),
+            u"54,463 mi",
+            u"5,446 mi",
+            u"545 mi",
+            u"54 mi",
+            u"5.4 mi",
+            u"0.54 mi",
+            u"96 yd",
+            u"9.6 yd",
+            u"0 yd");
+
+    formatter = unloc_formatter.locale("en-US");
+    formattedNum = formatter.formatDouble(300, status);
+    assertTrue(UnicodeString("unitUsage() en-US road, got outputUnit: \"") +
+                   formattedNum.getOutputUnit(status).getIdentifier() + "\"",
+               MeasureUnit::getFoot() == formattedNum.getOutputUnit(status));
+    assertEquals("unitUsage() en-US road", "984 ft", formattedNum.toString(status));
+    assertFormatDescendingBig(
+            u"unitUsage() en-US road",
+            u"measure-unit/length-meter usage/road",
+            u"unit/meter usage/road",
+            unloc_formatter,
+            Locale("en-US"),
+            u"54,463 mi",
+            u"5,446 mi",
+            u"545 mi",
+            u"54 mi",
+            u"5.4 mi",
+            u"0.54 mi",
+            u"288 ft",
+            u"29 ft",
+            u"0 ft");
+}
+
 void NumberFormatterApiTest::unitCompoundMeasure() {
     assertFormatDescending(
             u"Meters Per Second Short (unit that simplifies) and perUnit method",
diff --git a/icu4c/source/test/intltest/unitsdatatest.cpp b/icu4c/source/test/intltest/unitsdatatest.cpp
index d79ca78..3ac36f7 100644
--- a/icu4c/source/test/intltest/unitsdatatest.cpp
+++ b/icu4c/source/test/intltest/unitsdatatest.cpp
@@ -8,6 +8,8 @@
 #include "unitsdata.h"
 #include "intltest.h"
 
+using namespace ::icu::units;
+
 class UnitsDataTest : public IntlTest {
   public:
     UnitsDataTest() {}
diff --git a/icu4c/source/test/intltest/unitstest.cpp b/icu4c/source/test/intltest/unitstest.cpp
index cc31410..ba208ae 100644
--- a/icu4c/source/test/intltest/unitstest.cpp
+++ b/icu4c/source/test/intltest/unitstest.cpp
@@ -13,14 +13,17 @@
 #include "filestrm.h"
 #include "intltest.h"
 #include "number_decimalquantity.h"
+#include "putilimp.h"
 #include "unicode/ctest.h"
 #include "unicode/measunit.h"
 #include "unicode/unistr.h"
 #include "unicode/unum.h"
+#include "unicode/ures.h"
 #include "unitconverter.h"
 #include "unitsdata.h"
 #include "unitsrouter.h"
 #include "uparse.h"
+#include "uresimp.h"
 
 struct UnitConversionTestCase {
     const StringPiece source;
@@ -29,7 +32,8 @@
     const double expectedValue;
 };
 
-using icu::number::impl::DecimalQuantity;
+using ::icu::number::impl::DecimalQuantity;
+using namespace ::icu::units;
 
 class UnitsTest : public IntlTest {
   public:
@@ -37,6 +41,7 @@
 
     void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = NULL);
 
+    void testUnitConstantFreshness();
     void testConversionCapability();
     void testConversions();
     void testPreferences();
@@ -53,6 +58,7 @@
         logln("TestSuite UnitsTest: ");
     }
     TESTCASE_AUTO_BEGIN;
+    TESTCASE_AUTO(testUnitConstantFreshness);
     TESTCASE_AUTO(testConversionCapability);
     TESTCASE_AUTO(testConversions);
     TESTCASE_AUTO(testPreferences);
@@ -63,10 +69,58 @@
     TESTCASE_AUTO_END;
 }
 
+// Tests the hard-coded constants in the code against constants that appear in
+// units.txt.
+void UnitsTest::testUnitConstantFreshness() {
+    IcuTestErrorCode status(*this, "testUnitConstantFreshness");
+    LocalUResourceBundlePointer unitsBundle(ures_openDirect(NULL, "units", status));
+    LocalUResourceBundlePointer unitConstants(
+        ures_getByKey(unitsBundle.getAlias(), "unitConstants", NULL, status));
+
+    while (ures_hasNext(unitConstants.getAlias())) {
+        int32_t len;
+        const char *constant = NULL;
+        ures_getNextString(unitConstants.getAlias(), &len, &constant, status);
+
+        Factor factor;
+        addSingleFactorConstant(constant, 1, POSITIVE, factor, status);
+        if (status.errDataIfFailureAndReset(
+                "addSingleFactorConstant(<%s>, ...).\n\n"
+                "If U_INVALID_FORMAT_ERROR, please check that \"icu4c/source/i18n/unitconverter.cpp\" "
+                "has all constants? Is \"%s\" a new constant?\n",
+                constant, constant)) {
+            continue;
+        }
+
+        // Check the values of constants that have a simple numeric value
+        factor.substituteConstants();
+        int32_t uLen;
+        UnicodeString uVal = ures_getStringByKey(unitConstants.getAlias(), constant, &uLen, status);
+        CharString val;
+        val.appendInvariantChars(uVal, status);
+        if (status.errDataIfFailureAndReset("Failed to get constant value for %s.", constant)) {
+            continue;
+        }
+        DecimalQuantity dqVal;
+        UErrorCode parseStatus = U_ZERO_ERROR;
+        // TODO(units): unify with strToDouble() in unitconverter.cpp
+        dqVal.setToDecNumber(val.toStringPiece(), parseStatus);
+        if (!U_SUCCESS(parseStatus)) {
+            // Not simple to parse, skip validating this constant's value. (We
+            // leave catching mistakes to the data-driven integration tests.)
+            continue;
+        }
+        double expectedNumerator = dqVal.toDouble();
+        assertEquals(UnicodeString("Constant ") + constant + u" numerator", expectedNumerator,
+                     factor.factorNum);
+        assertEquals(UnicodeString("Constant ") + constant + u" denominator", 1.0, factor.factorDen);
+    }
+}
+
 void UnitsTest::testConversionCapability() {
     struct TestCase {
-        const StringPiece source;
-        const StringPiece target;
+        const char *const source;
+        const char *const target;
         const UnitsConvertibilityState expectedState;
     } testCases[]{
         {"meter", "foot", CONVERTIBLE},                                         //
@@ -75,7 +129,7 @@
         {"kilometer-per-second", "second-per-meter", RECIPROCAL},               //
         {"square-meter", "square-foot", CONVERTIBLE},                           //
         {"kilometer-per-second", "foot-per-second", CONVERTIBLE},               //
-        {"square-hectare", "p4-foot", CONVERTIBLE},                             //
+        {"square-hectare", "pow4-foot", CONVERTIBLE},                           //
         {"square-kilometer-per-second", "second-per-square-meter", RECIPROCAL}, //
     };
 
@@ -86,9 +140,11 @@
         MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
 
         ConversionRates conversionRates(status);
-        auto convertibility = icu::checkConvertibility(source, target, conversionRates, status);
+        auto convertibility = checkConvertibility(source, target, conversionRates, status);
 
-        assertEquals("Conversion Capability", testCase.expectedState, convertibility);
+        assertEquals(UnicodeString("Conversion Capability: ") + testCase.source + " to " +
+                         testCase.target,
+                     testCase.expectedState, convertibility);
     }
 }
 
@@ -96,8 +152,8 @@
     IcuTestErrorCode status(*this, "Units testSiPrefixes");
     // Test Cases
     struct TestCase {
-        StringPiece source;
-        StringPiece target;
+        const char *source;
+        const char *target;
         const double inputValue;
         const double expectedValue;
     } testCases[]{
@@ -118,15 +174,12 @@
         MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
         MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
 
-        MaybeStackVector<MeasureUnit> units;
-        units.emplaceBack(source);
-        units.emplaceBack(target);
-
         ConversionRates conversionRates(status);
         UnitConverter converter(source, target, conversionRates, status);
 
-        assertEqualsNear("test conversion", testCase.expectedValue,
-                         converter.convert(testCase.inputValue), 0.001);
+        assertEqualsNear(UnicodeString("testSiPrefixes: ") + testCase.source + " to " + testCase.target,
+                         testCase.expectedValue, converter.convert(testCase.inputValue),
+                         0.0001 * testCase.expectedValue);
     }
 }
 
@@ -135,8 +188,8 @@
 
     // Test Cases
     struct TestCase {
-        StringPiece source;
-        StringPiece target;
+        const char *source;
+        const char *target;
         const double inputValue;
         const double expectedValue;
     } testCases[]{
@@ -156,15 +209,12 @@
         MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
         MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
 
-        MaybeStackVector<MeasureUnit> units;
-        units.emplaceBack(source);
-        units.emplaceBack(target);
-
         ConversionRates conversionRates(status);
         UnitConverter converter(source, target, conversionRates, status);
 
-        assertEqualsNear("test conversion", testCase.expectedValue,
-                         converter.convert(testCase.inputValue), 0.001);
+        assertEqualsNear(UnicodeString("testMass: ") + testCase.source + " to " + testCase.target,
+                         testCase.expectedValue, converter.convert(testCase.inputValue),
+                         0.0001 * testCase.expectedValue);
     }
 }
 
@@ -172,8 +222,8 @@
     IcuTestErrorCode status(*this, "Units testTemperature");
     // Test Cases
     struct TestCase {
-        StringPiece source;
-        StringPiece target;
+        const char *source;
+        const char *target;
         const double inputValue;
         const double expectedValue;
     } testCases[]{
@@ -193,15 +243,12 @@
         MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
         MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
 
-        MaybeStackVector<MeasureUnit> units;
-        units.emplaceBack(source);
-        units.emplaceBack(target);
-
         ConversionRates conversionRates(status);
         UnitConverter converter(source, target, conversionRates, status);
 
-        assertEqualsNear("test conversion", testCase.expectedValue,
-                         converter.convert(testCase.inputValue), 0.001);
+        assertEqualsNear(UnicodeString("testTemperature: ") + testCase.source + " to " + testCase.target,
+                         testCase.expectedValue, converter.convert(testCase.inputValue),
+                         0.0001 * uprv_fabs(testCase.expectedValue));
     }
 }
 
@@ -210,8 +257,8 @@
 
     // Test Cases
     struct TestCase {
-        StringPiece source;
-        StringPiece target;
+        const char *source;
+        const char *target;
         const double inputValue;
         const double expectedValue;
     } testCases[]{
@@ -234,15 +281,12 @@
         MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
         MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
 
-        MaybeStackVector<MeasureUnit> units;
-        units.emplaceBack(source);
-        units.emplaceBack(target);
-
         ConversionRates conversionRates(status);
         UnitConverter converter(source, target, conversionRates, status);
 
-        assertEqualsNear("test conversion", testCase.expectedValue,
-                         converter.convert(testCase.inputValue), 0.001);
+        assertEqualsNear(UnicodeString("testArea: ") + testCase.source + " to " + testCase.target,
+                         testCase.expectedValue, converter.convert(testCase.inputValue),
+                         0.0001 * testCase.expectedValue);
     }
 }
 
@@ -353,7 +397,7 @@
     double got = converter.convert(1000);
     msg.clear();
     msg.append("Converting 1000 ", status).append(x, status).append(" to ", status).append(y, status);
-    unitsTest->assertEqualsNear(msg.data(), expected, got, 0.0001);
+    unitsTest->assertEqualsNear(msg.data(), expected, got, 0.0001 * expected);
 }
 
 /**
@@ -469,38 +513,14 @@
     }
 };
 
-// TODO(Hugo): Add a comment and Use AssertEqualsNear.
+// Checks a vector of Measure instances against ExpectedOutput.
 void checkOutput(UnitsTest *unitsTest, const char *msg, ExpectedOutput expected,
                  const MaybeStackVector<Measure> &actual, double precision) {
     IcuTestErrorCode status(*unitsTest, "checkOutput");
-    bool success = true;
-    if (expected._compoundCount != actual.length()) {
-        success = false;
-    }
-    for (int i = 0; i < actual.length(); i++) {
-        if (i >= expected._compoundCount) {
-            break;
-        }
 
-        // assertEqualsNear("test conversion", expected._amounts[i],
-        //                 actual[i]->getNumber().getDouble(status), 0.0001);
-
-        double diff = std::abs(expected._amounts[i] - actual[i]->getNumber().getDouble(status));
-        double diffPercent = expected._amounts[i] != 0 ? diff / expected._amounts[i] : diff;
-        if (diffPercent > precision) {
-            success = false;
-            break;
-        }
-
-        if (expected._measureUnits[i] != actual[i]->getUnit()) {
-            success = false;
-            break;
-        }
-    }
-
-    CharString testMessage("test case: ", status);
+    CharString testMessage("Test case \"", status);
     testMessage.append(msg, status);
-    testMessage.append(", expected output: ", status);
+    testMessage.append("\": expected output: ", status);
     testMessage.append(expected.toDebugString().c_str(), status);
     testMessage.append(", obtained output:", status);
     for (int i = 0; i < actual.length(); i++) {
@@ -509,8 +529,19 @@
         testMessage.append(" ", status);
         testMessage.appendInvariantChars(actual[i]->getUnit().getIdentifier(), status);
     }
-
-    unitsTest->assertTrue(testMessage.data(), success);
+    if (!unitsTest->assertEquals(testMessage.data(), expected._compoundCount, actual.length())) {
+        return;
+    };
+    for (int i = 0; i < actual.length(); i++) {
+        double permittedDiff = precision * expected._amounts[i];
+        if (permittedDiff == 0) {
+            // If 0 is expected, still permit a small delta.
+            // TODO: revisit this experimentally chosen value:
+            permittedDiff = 0.00000001;
+        }
+        unitsTest->assertEqualsNear(testMessage.data(), expected._amounts[i],
+                                    actual[i]->getNumber().getDouble(status), permittedDiff);
+    }
 }
 
 /**
@@ -590,7 +621,8 @@
     if (status.errIfFailureAndReset("router.route(inputAmount, ...)")) {
         return;
     }
-    checkOutput(unitsTest, msg.data(), expected, result, 0.0001);
+    // TODO: revisit this experimentally chosen precision:
+    checkOutput(unitsTest, msg.data(), expected, result, 0.0000000001);
 }
 
 /**