[device_settings] Use fuchsia.update.ChannelControl.

Bug: 37233
Test: fx run-host-tests device_settings_tests

Change-Id: Ia050d51a38d7234f59fc016c09b8e67c7cbd5d53
diff --git a/settings/device/BUILD.gn b/settings/device/BUILD.gn
index 2f94679..7044a00 100644
--- a/settings/device/BUILD.gn
+++ b/settings/device/BUILD.gn
@@ -25,10 +25,9 @@
   ]
 
   deps = [
-    "//sdk/fidl/fuchsia.pkg",
-    "//sdk/fidl/fuchsia.pkg.rewrite",
     "//sdk/fidl/fuchsia.recovery",
     "//sdk/fidl/fuchsia.update",
+    "//sdk/fidl/fuchsia.update.channelcontrol",
     "//third_party/dart-pkg/git/flutter/packages/flutter",
     "//topaz/lib/settings:lib.settings",
     "//topaz/public/dart/fuchsia_logger",
diff --git a/settings/device/lib/model.dart b/settings/device/lib/model.dart
index 80e8f56..3ff035a 100644
--- a/settings/device/lib/model.dart
+++ b/settings/device/lib/model.dart
@@ -4,10 +4,9 @@
 
 import 'dart:async';
 
-import 'package:fidl/fidl.dart';
 import 'package:fidl_fuchsia_update/fidl_async.dart' as update;
-import 'package:fidl_fuchsia_pkg/fidl_async.dart' as pkg;
-import 'package:fidl_fuchsia_pkg_rewrite/fidl_async.dart' as pkg_rewrite;
+import 'package:fidl_fuchsia_update_channelcontrol/fidl_async.dart'
+    as channelcontrol;
 import 'package:fidl_fuchsia_recovery/fidl_async.dart' as recovery;
 import 'package:flutter/foundation.dart';
 import 'package:fuchsia_logger/logger.dart';
@@ -27,14 +26,11 @@
 
   Future<bool> checkForSystemUpdate();
 
-  Stream<pkg.RepositoryConfig> listRepositories();
+  Future<String> getCurrentChannel();
 
-  Stream<pkg_rewrite.Rule> listRules();
+  Future<void> setTargetChannel(String channel);
 
-  Stream<pkg_rewrite.Rule> listStaticRules();
-
-  Future<int> updateRules(
-      Iterable<pkg_rewrite.Rule> Function(List<pkg_rewrite.Rule>) action);
+  Future<List<String>> getChannelList();
 
   Future<void> factoryReset();
 
@@ -45,20 +41,16 @@
   /// Controller for the update manager service.
   final update.ManagerProxy _updateManager = update.ManagerProxy();
 
-  /// Controller for package repo manager and rewrite engine (our update service).
-  final pkg.RepositoryManagerProxy _repositoryManager =
-      pkg.RepositoryManagerProxy();
-  final pkg_rewrite.EngineProxy _rewriteManager = pkg_rewrite.EngineProxy();
+  /// Controller for the update channel control service.
+  final channelcontrol.ChannelControlProxy _channelControl =
+      channelcontrol.ChannelControlProxy();
 
   /// Controller for the factory reset service.
   final recovery.FactoryResetProxy _factoryReset = recovery.FactoryResetProxy();
 
   DefaultSystemInterfaceImpl() {
     StartupContext.fromStartupInfo().incoming.connectToService(_updateManager);
-    StartupContext.fromStartupInfo()
-        .incoming
-        .connectToService(_repositoryManager);
-    StartupContext.fromStartupInfo().incoming.connectToService(_rewriteManager);
+    StartupContext.fromStartupInfo().incoming.connectToService(_channelControl);
   }
 
   @override
@@ -74,65 +66,14 @@
   }
 
   @override
-  Stream<pkg.RepositoryConfig> listRepositories() async* {
-    final iter = pkg.RepositoryIteratorProxy();
-    await _repositoryManager.list(iter.ctrl.request());
-
-    List<pkg.RepositoryConfig> repos;
-    do {
-      repos = await iter.next();
-      for (var repo in repos) {
-        yield repo;
-      }
-    } while (repos.isNotEmpty);
-  }
+  Future<String> getCurrentChannel() => _channelControl.getCurrent();
 
   @override
-  Stream<pkg_rewrite.Rule> listRules() {
-    return _listRules(_rewriteManager.list);
-  }
+  Future<void> setTargetChannel(String channel) =>
+      _channelControl.setTarget(channel);
 
   @override
-  Stream<pkg_rewrite.Rule> listStaticRules() {
-    return _listRules(_rewriteManager.listStatic);
-  }
-
-  Future<int> editRuleTransaction(
-      Future<void> Function(pkg_rewrite.EditTransaction) action) async {
-    // In most cases this loop will only run once, but to be safe against
-    // other writers we loop a few times.
-    for (var i = 0; i < 10; i++) {
-      final transaction = pkg_rewrite.EditTransactionProxy();
-      await _rewriteManager.startEditTransaction(transaction.ctrl.request());
-      await action(transaction);
-      final status = await transaction.commit();
-      switch (status) {
-        case ZX.OK:
-          return ZX.OK;
-        case ZX.ERR_UNAVAILABLE:
-          continue;
-        default:
-          return status;
-      }
-    }
-    return ZX.ERR_UNAVAILABLE;
-  }
-
-  @override
-  Future<int> updateRules(
-      Iterable<pkg_rewrite.Rule> Function(List<pkg_rewrite.Rule>)
-          action) async {
-    return editRuleTransaction((tx) async {
-      final rules = <pkg_rewrite.Rule>[];
-      await _listRules(tx.listDynamic).forEach(rules.add);
-
-      await tx.resetAll();
-
-      for (var rule in action(rules).toList().reversed) {
-        await tx.add(rule);
-      }
-    });
-  }
+  Future<List<String>> getChannelList() => _channelControl.getTargetList();
 
   @override
   Future<void> factoryReset() async {
@@ -146,8 +87,7 @@
   @override
   void dispose() {
     _updateManager.ctrl.close();
-    _repositoryManager.ctrl.close();
-    _rewriteManager.ctrl.close();
+    _channelControl.ctrl.close();
     _factoryReset.ctrl.close();
   }
 }
@@ -179,10 +119,6 @@
 
   ValueNotifier<bool> channelPopupShowing = ValueNotifier<bool>(false);
 
-  final List<pkg.RepositoryConfig> _repos = [];
-  final List<pkg_rewrite.Rule> _rules = [];
-  final List<pkg_rewrite.Rule> _staticRules = [];
-
   bool _isChannelUpdating = false;
 
   SystemInterface _sysInterface;
@@ -194,23 +130,13 @@
 
   DateTime get lastUpdate => _lastUpdate;
 
-  List<pkg.RepositoryConfig> get repos => _repos;
+  String _currentChannel;
 
-  List<pkg_rewrite.Rule> get rules => _rules;
+  String get currentChannel => _currentChannel;
 
-  String get currentChannel {
-    final rule =
-        rules.firstWhere(_ruleIsFuchsiaReplacement, orElse: () => null);
+  List<String> _channels;
 
-    return rule?.literal?.hostReplacement;
-  }
-
-  String get defaultChannel {
-    final rule =
-        _staticRules.firstWhere(_ruleIsFuchsiaReplacement, orElse: () => null);
-
-    return rule?.literal?.hostReplacement;
-  }
+  List<String> get channels => _channels;
 
   /// Determines whether the confirmation dialog for factory reset should
   /// be displayed.
@@ -235,54 +161,12 @@
     _lastUpdate = DateTime.now();
   }
 
-  Future<void> selectChannel(pkg.RepositoryConfig selectedConfig) async {
-    log.info('selecting channel ${selectedConfig.repoUrl}');
+  Future<void> selectChannel(String selectedChannel) async {
+    log.info('selecting channel $selectedChannel');
     channelPopupShowing.value = false;
     _setChannelState(updating: true);
 
-    final repoUrl = Uri.parse(selectedConfig.repoUrl);
-    final pkg_rewrite.Rule selectedRule = pkg_rewrite.Rule.withLiteral(
-        pkg_rewrite.LiteralRule(
-            hostMatch: 'fuchsia.com',
-            hostReplacement: repoUrl.host,
-            pathPrefixMatch: '/',
-            pathPrefixReplacement: '/'));
-
-    final status = await _sysInterface.updateRules((rules) sync* {
-      // Find the first fuchsia.com rule. If it doesn't exist, add it to the
-      // front of the rule set. Otherwise replace it, and filter out any
-      // duplicates.
-      final index = rules.indexWhere(_ruleIsFuchsiaReplacement);
-      if (index == -1) {
-        yield selectedRule;
-        yield* rules;
-      } else {
-        yield* rules.getRange(0, index);
-        yield selectedRule;
-        yield* rules
-            .getRange(index + 1, rules.length)
-            .where((rule) => !_ruleIsFuchsiaReplacement(rule));
-      }
-    });
-
-    if (status != ZX.OK) {
-      log.severe('error while modifying rewrite rules: $status');
-    }
-
-    await _update();
-  }
-
-  Future<void> clearChannel() async {
-    log.info('clearing channel');
-    channelPopupShowing.value = false;
-    _setChannelState(updating: true);
-
-    final status = await _sysInterface.updateRules(
-        (rules) => rules.where((rule) => !_ruleIsFuchsiaReplacement(rule)));
-
-    if (status != ZX.OK) {
-      log.severe('error while modifying rewrite rules: $status');
-    }
+    await _sysInterface.setTargetChannel(selectedChannel);
 
     await _update();
   }
@@ -290,14 +174,9 @@
   Future<void> _update() async {
     _setChannelState(updating: true);
 
-    _repos.clear();
-    await _sysInterface.listRepositories().forEach(_repos.add);
+    _currentChannel = await _sysInterface.getCurrentChannel();
 
-    _rules.clear();
-    await _sysInterface.listRules().forEach(_rules.add);
-
-    _staticRules.clear();
-    await _sysInterface.listStaticRules().forEach(_staticRules.add);
+    _channels = await _sysInterface.getChannelList();
 
     _setChannelState(updating: false);
   }
@@ -355,24 +234,3 @@
     notifyListeners();
   }
 }
-
-Stream<pkg_rewrite.Rule> _listRules(
-    Future<void> Function(InterfaceRequest<pkg_rewrite.RuleIterator>)
-        action) async* {
-  final iter = pkg_rewrite.RuleIteratorProxy();
-  await action(iter.ctrl.request());
-
-  List<pkg_rewrite.Rule> rules;
-  do {
-    rules = await iter.next();
-    for (var rule in rules) {
-      yield rule;
-    }
-  } while (rules.isNotEmpty);
-}
-
-bool _ruleIsFuchsiaReplacement(pkg_rewrite.Rule rule) {
-  return rule.literal != null &&
-      rule.literal.hostMatch == 'fuchsia.com' &&
-      rule.literal.pathPrefixMatch == '/';
-}
diff --git a/settings/device/lib/src/widget.dart b/settings/device/lib/src/widget.dart
index 8648844..80fd5c7 100644
--- a/settings/device/lib/src/widget.dart
+++ b/settings/device/lib/src/widget.dart
@@ -109,25 +109,18 @@
             widthFactor: 0.8,
             heightFactor: 0.8,
             child: SingleChildScrollView(
-              physics: BouncingScrollPhysics(),
-              padding: EdgeInsets.all(16.0),
-              child: SettingsSection(
-                  title: 'Select channel',
-                  scale: scale,
-                  child: Column(
-                      children: model.repos
-                          .map((repo) => SettingsButton(
-                              onTap: () => model.selectChannel(repo),
-                              scale: scale,
-                              text: '${repo?.repoUrl ?? 'None'}'))
-                          .toList()
-                            ..add(SettingsButton(
-                                onTap: () => model.clearChannel(),
+                physics: BouncingScrollPhysics(),
+                padding: EdgeInsets.all(16.0),
+                child: SettingsSection(
+                    title: 'Select channel',
+                    scale: scale,
+                    child: Column(
+                        children: model.channels
+                            .map((channel) => SettingsButton(
+                                onTap: () => model.selectChannel(channel),
                                 scale: scale,
-                                text: model.defaultChannel == null
-                                    ? 'None'
-                                    : 'Default (${model.defaultChannel})')))),
-            ),
+                                text: '$channel'))
+                            .toList()))),
           )));
 }
 
diff --git a/settings/device/meta/device_settings.cmx b/settings/device/meta/device_settings.cmx
index a6664c9..455735c 100644
--- a/settings/device/meta/device_settings.cmx
+++ b/settings/device/meta/device_settings.cmx
@@ -15,13 +15,12 @@
             "fuchsia.fonts.Provider",
             "fuchsia.logger.LogSink",
             "fuchsia.modular.Clipboard",
-            "fuchsia.pkg.RepositoryManager",
-            "fuchsia.pkg.rewrite.Engine",
             "fuchsia.recovery.FactoryReset",
             "fuchsia.sys.Environment",
             "fuchsia.ui.input.ImeService",
             "fuchsia.ui.policy.Presenter",
             "fuchsia.ui.scenic.Scenic",
+            "fuchsia.update.channelcontrol.ChannelControl",
             "fuchsia.update.Manager"
         ]
     }
diff --git a/settings/device/test/device_settings_model_test.dart b/settings/device/test/device_settings_model_test.dart
index f4623dc..10499bc 100644
--- a/settings/device/test/device_settings_model_test.dart
+++ b/settings/device/test/device_settings_model_test.dart
@@ -5,10 +5,10 @@
 import 'dart:async';
 
 import 'package:device_settings/model.dart';
-import 'package:fidl_fuchsia_pkg/fidl_async.dart' as pkg;
-import 'package:fidl_fuchsia_pkg_rewrite/fidl_async.dart' as pkg_rewrite;
 import 'package:fidl_fuchsia_recovery/fidl_async.dart' as recovery;
 import 'package:fidl_fuchsia_update/fidl_async.dart' as update;
+import 'package:fidl_fuchsia_update_channelcontrol/fidl_async.dart'
+    as channelcontrol;
 import 'package:mockito/mockito.dart';
 import 'package:test/test.dart';
 
@@ -22,11 +22,8 @@
 
 class MockUpdateManager extends Mock implements update.Manager {}
 
-class MockRepositoryManager extends Mock implements pkg.RepositoryManager {}
-
-class MockRewriteManager extends Mock implements pkg_rewrite.Engine {}
-
-class MockRepositoryIterator extends Mock implements pkg.RepositoryIterator {}
+class MockChannelControl extends Mock implements channelcontrol.ChannelControl {
+}
 
 class MockFactoryReset extends Mock implements recovery.FactoryReset {}
 
@@ -35,41 +32,33 @@
   test('test_start', () async {
     final TestSystemInterface sysInterface = TestSystemInterface();
 
-    when(sysInterface.listRepositories())
-        .thenAnswer((_) => Stream.fromIterable([]));
+    when(sysInterface.getCurrentChannel()).thenAnswer((_) => Future.value(''));
 
-    when(sysInterface.listRules()).thenAnswer((_) => Stream.fromIterable([]));
-
-    when(sysInterface.listStaticRules())
-        .thenAnswer((_) => Stream.fromIterable([]));
+    when(sysInterface.getChannelList()).thenAnswer((_) => Future.value([]));
 
     final DeviceSettingsModel model = DeviceSettingsModel(sysInterface);
     await model.start();
 
     // Ensure resolver is interacted with on the first start.
-    verify(sysInterface.listRepositories());
-    verify(sysInterface.listRules());
-    verify(sysInterface.listStaticRules());
+    verify(sysInterface.getCurrentChannel());
+    verify(sysInterface.getChannelList());
 
     // We should not be waiting on anything in the second start as it should be
     // an early return.
     await model.start();
 
     // Ensure resolver has not been interacted with since the first start.
-    verifyNever(sysInterface.listRepositories());
-    verifyNever(sysInterface.listRules());
-    verifyNever(sysInterface.listStaticRules());
+    verifyNever(sysInterface.getCurrentChannel());
+    verifyNever(sysInterface.getChannelList());
   });
 
   // Ensure checkForSystemUpdate has the intended side effects.
   test('test_check_for_updates', () async {
     final TestSystemInterface sysInterface = TestSystemInterface();
 
-    when(sysInterface.listRepositories())
-        .thenAnswer((_) => Stream.fromIterable([]));
-    when(sysInterface.listRules()).thenAnswer((_) => Stream.fromIterable([]));
-    when(sysInterface.listStaticRules())
-        .thenAnswer((_) => Stream.fromIterable([]));
+    when(sysInterface.getCurrentChannel()).thenAnswer((_) => Future.value(''));
+
+    when(sysInterface.getChannelList()).thenAnswer((_) => Future.value([]));
 
     when(sysInterface.checkForSystemUpdate())
         .thenAnswer((_) => Future.value(true));
@@ -94,25 +83,14 @@
   test('test_channel_updating_state', () async {
     final TestSystemInterface sysInterface = TestSystemInterface();
 
-    var repoCompleter = Completer();
-    var ruleCompleter = Completer();
-    var staticRuleCompleter = Completer();
+    var currentChannelCompleter = Completer<String>();
+    var channelListCompleter = Completer<List<String>>();
 
-    when(sysInterface.listRepositories()).thenAnswer((_) async* {
-      for (var repo in await repoCompleter.future) {
-        yield repo;
-      }
-    });
-    when(sysInterface.listRules()).thenAnswer((_) async* {
-      for (var rule in await ruleCompleter.future) {
-        yield rule;
-      }
-    });
-    when(sysInterface.listStaticRules()).thenAnswer((_) async* {
-      for (var rule in await staticRuleCompleter.future) {
-        yield rule;
-      }
-    });
+    when(sysInterface.getCurrentChannel())
+        .thenAnswer((_) => currentChannelCompleter.future);
+
+    when(sysInterface.getChannelList())
+        .thenAnswer((_) => channelListCompleter.future);
 
     final DeviceSettingsModel model = DeviceSettingsModel(sysInterface);
     final Future startFuture = model.start();
@@ -120,28 +98,22 @@
     // On start, the model should report it is updating as the proxy has not
     // returned
     expect(model.channelUpdating, true);
-    repoCompleter.complete([]);
-    ruleCompleter.complete([]);
-    staticRuleCompleter.complete([]);
+    currentChannelCompleter.complete('');
+    channelListCompleter.complete([]);
     await startFuture;
     expect(model.channelUpdating, false);
 
     // Reset update completer so it can be used in the next step.
-    repoCompleter = Completer();
-    ruleCompleter = Completer();
-    staticRuleCompleter = Completer();
+    currentChannelCompleter = Completer<String>();
+    channelListCompleter = Completer<List<String>>();
 
-    when(sysInterface.updateRules(any)).thenAnswer((_) async {
-      return 0;
-    });
+    when(sysInterface.setTargetChannel(any)).thenAnswer((_) async {});
 
     // make sure we are also updating when selecting a channel.
-    Future selectFuture = model.selectChannel(
-        pkg.RepositoryConfig(repoUrl: 'fuchsia-pkg://example.com'));
+    Future selectFuture = model.selectChannel('stable');
     expect(model.channelUpdating, true);
-    repoCompleter.complete([]);
-    ruleCompleter.complete([]);
-    staticRuleCompleter.complete([]);
+    currentChannelCompleter.complete('');
+    channelListCompleter.complete([]);
     await selectFuture;
     expect(model.channelUpdating, false);
   });
@@ -150,13 +122,9 @@
   test('test_check_for_factory_reset', () async {
     final TestSystemInterface sysInterface = TestSystemInterface();
 
-    when(sysInterface.listRepositories())
-        .thenAnswer((_) => Stream.fromIterable([]));
+    when(sysInterface.getCurrentChannel()).thenAnswer((_) => Future.value(''));
 
-    when(sysInterface.listRules()).thenAnswer((_) => Stream.fromIterable([]));
-
-    when(sysInterface.listStaticRules())
-        .thenAnswer((_) => Stream.fromIterable([]));
+    when(sysInterface.getChannelList()).thenAnswer((_) => Future.value([]));
 
     final DeviceSettingsModel model = DeviceSettingsModel(sysInterface);
     await model.start();
@@ -179,13 +147,9 @@
   test('test_check_for_factory_reset', () async {
     final TestSystemInterface sysInterface = TestSystemInterface();
 
-    when(sysInterface.listRepositories())
-        .thenAnswer((_) => Stream.fromIterable([]));
+    when(sysInterface.getCurrentChannel()).thenAnswer((_) => Future.value(''));
 
-    when(sysInterface.listRules()).thenAnswer((_) => Stream.fromIterable([]));
-
-    when(sysInterface.listStaticRules())
-        .thenAnswer((_) => Stream.fromIterable([]));
+    when(sysInterface.getChannelList()).thenAnswer((_) => Future.value([]));
 
     final DeviceSettingsModel model = DeviceSettingsModel(sysInterface);
     await model.start();