← Back to team overview

usb-creator-hackers team mailing list archive

[Merge] lp:~xnox/usb-creator/nexus7 into lp:usb-creator

 

Dmitrijs Ledkovs has proposed merging lp:~xnox/usb-creator/nexus7 into lp:usb-creator.

Requested reviews:
  James Hunt (jamesodhunt)
  The Ubuntu Nexus7 Team (ubuntu-nexus7)
  usb-creator hackers (usb-creator-hackers)

For more details, see:
https://code.launchpad.net/~xnox/usb-creator/nexus7/+merge/144753
-- 
https://code.launchpad.net/~xnox/usb-creator/nexus7/+merge/144753
Your team usb-creator hackers is requested to review the proposed merge of lp:~xnox/usb-creator/nexus7 into lp:usb-creator.
=== modified file 'bin/usb-creator-gtk'
--- bin/usb-creator-gtk	2012-05-04 09:29:27 +0000
+++ bin/usb-creator-gtk	2013-01-24 17:05:29 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 
 # Copyright (C) 2009 Canonical Ltd.
 
@@ -27,6 +27,7 @@
     os.environ['USBCREATOR_LOCAL'] = '1'
 from usbcreator.frontends.gtk import GtkFrontend
 from usbcreator.backends.udisks import UDisksBackend
+from usbcreator.backends.fastboot import FastbootBackend
 from usbcreator.misc import sane_path, setup_gettext, setup_logging, text_type
 
 sane_path()
@@ -56,13 +57,23 @@
                   help=_('allow writing to system-internal devices'))
 parser.add_option('--show-all', dest='show_all', action='store_true',
                   help=_('Show all devices'))
+parser.add_option('--fastboot', dest='fastboot', action='store_true',
+                  help=_('Use fastboot backend to flash Android devices.'))
 (options, args) = parser.parse_args()
 
 try:
-    backend = UDisksBackend(allow_system_internal=options.allow_system_internal,
-                            show_all=options.show_all)
-    frontend = GtkFrontend(backend, options.img, options.persistent,
-                           allow_system_internal=options.allow_system_internal)
+    if options.fastboot:
+        options.persistent = False
+        backend = FastbootBackend()
+    else:
+        backend = UDisksBackend(
+            allow_system_internal=options.allow_system_internal,
+            show_all=options.show_all)
+    frontend = GtkFrontend(
+        backend, options.img, options.persistent,
+        allow_system_internal=options.allow_system_internal,
+        fastboot_mode=options.fastboot,
+        )
 except DBusException as e:
     # FIXME evand 2009-07-09: Wouldn't service activation pick this up
     # automatically?

=== modified file 'debian/changelog'
--- debian/changelog	2013-01-17 18:01:26 +0000
+++ debian/changelog	2013-01-24 17:05:29 +0000
@@ -1,3 +1,9 @@
+usb-creator (0.2.45) UNRELEASED; urgency=low
+
+  * Support flashing devices using fastboot (e.g. Nexus7).
+
+ -- Dmitrijs Ledkovs <dmitrij.ledkov@xxxxxxxxxx>  Wed, 16 Jan 2013 13:54:16 +0000
+
 usb-creator (0.2.44) raring; urgency=low
 
   * Revert sync remount flag. It's too slow.

=== modified file 'debian/rules'
--- debian/rules	2012-11-09 00:42:18 +0000
+++ debian/rules	2013-01-24 17:05:29 +0000
@@ -35,6 +35,9 @@
 	find . -path "$(vendor_dir)/*.svg" -exec cp {} $(dest) \;
 	dh_install
 
+override_dh_installinit:
+	dh_installinit --upstart-only
+
 override_dh_installmenu:
 ifneq (,$(wildcard $(vendor_dir)/*.xpm))
 	mkdir -p $(xpm_gtk) $(xpm_kde)

=== modified file 'debian/usb-creator-common.install'
--- debian/usb-creator-common.install	2010-05-14 16:50:14 +0000
+++ debian/usb-creator-common.install	2013-01-24 17:05:29 +0000
@@ -5,6 +5,7 @@
 usr/lib/python*/*/usbcreator/frontends/base/*
 usr/lib/python*/*/usbcreator/*.py
 usr/share/usb-creator/usb-creator-helper
+usr/share/usb-creator/ubuntu-nexus7-USAGE-NOTICE-en.txt
 usr/share/polkit-1/actions/*
 usr/share/dbus-1/system-services/*
 etc/dbus-1/system.d/*

=== added file 'debian/usb-creator-gtk.upstart'
--- debian/usb-creator-gtk.upstart	1970-01-01 00:00:00 +0000
+++ debian/usb-creator-gtk.upstart	2013-01-24 17:05:29 +0000
@@ -0,0 +1,24 @@
+# usb-creator-gtk
+
+# This really should be enchanced user session job
+# and not a system one!
+# Then the hacks to get DISPLAY & user can go away.
+# Also the udev test are ugly =/
+
+author "Dmitrijs Ledkovs <xnox@xxxxxxxxxx>"
+
+start on (usb-device-added)
+pre-start script
+  [ x"$ID_VENDOR_ID" != x"18d1" ] && { stop; exit 0; }
+  [ x"$ID_MODEL_ID" != x"4e40" ] && { [ x"$ID_MODEL_ID" != x"d001" ] && { stop; exit 0; } }
+end script
+
+script
+test -f /usr/share/acpi-support/power-funcs || exit 0
+. /usr/share/acpi-support/power-funcs
+
+getXconsole
+if [ x"$XAUTHORITY" != x"" ]; then
+	sudo -u $user DISPLAY="$DISPLAY" usb-creator-gtk --fastboot
+fi
+end script

=== added file 'gui/ubuntu-nexus7-USAGE-NOTICE-en.txt'
--- gui/ubuntu-nexus7-USAGE-NOTICE-en.txt	1970-01-01 00:00:00 +0000
+++ gui/ubuntu-nexus7-USAGE-NOTICE-en.txt	2013-01-24 17:05:29 +0000
@@ -0,0 +1,21 @@
+"Ubuntu for Google Nexus 7" is released for free non-commercial use on 
+the Google Nexus 7 only. It is provided without warranty, even the 
+implied warranty of merchantability, satisfaction or fitness for a 
+particular use. See the licence included with each program for details. 
+ Some licences may grant additional rights; this notice shall not limit 
+your rights under each program's licence. Licences for each program are 
+available in the usr/share/doc directory. Source code for Ubuntu can be 
+downloaded from archive.ubuntu.com. Ubuntu, the Ubuntu logo and 
+Canonical are registered trademarks of Canonical Ltd. All other 
+trademarks are the property of their respective owners.
+
+"Ubuntu for Google Nexus 7" is released for limited use due to the 
+inclusion of the following hardware drivers:
+
+[Wireless] - [Broadcom Corporation]
+[Bluetooth] - [Broadcom Corporation]
+[Graphics] - [NVIDIA Corporation]
+[Video Codecs] - [NVIDIA Corporation]
+
+The original components and licenses can be found at:
+https://developers.google.com/android/nexus/drivers#grouper

=== modified file 'gui/usbcreator-gtk.ui'
--- gui/usbcreator-gtk.ui	2012-02-20 20:05:55 +0000
+++ gui/usbcreator-gtk.ui	2013-01-24 17:05:29 +0000
@@ -1,13 +1,56 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.24"/>
-  <!-- interface-naming-policy toplevel-contextual -->
+  <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkAdjustment" id="adjustment1">
     <property name="upper">100</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
     <property name="page_size">10</property>
   </object>
+  <object class="GtkDialog" id="eula_dialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Legal Notice</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox6">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area6">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkTextView" id="eula_text">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="vexpand">True</property>
+            <property name="editable">False</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
   <object class="GtkDialog" id="failed_dialog">
     <property name="can_focus">True</property>
     <property name="title" translatable="yes">Installation Failed</property>
@@ -18,11 +61,11 @@
     <property name="type_hint">dialog</property>
     <property name="gravity">center</property>
     <child internal-child="vbox">
-      <object class="GtkVBox" id="dialog-vbox5">
+      <object class="GtkBox" id="dialog-vbox5">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <child internal-child="action_area">
-          <object class="GtkHButtonBox" id="dialog-action_area5">
+          <object class="GtkButtonBox" id="dialog-action_area5">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="layout_style">end</property>
@@ -33,7 +76,6 @@
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
                 <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
               </object>
               <packing>
@@ -123,11 +165,11 @@
     <property name="type_hint">dialog</property>
     <property name="gravity">center</property>
     <child internal-child="vbox">
-      <object class="GtkVBox" id="dialog-vbox3">
+      <object class="GtkBox" id="dialog-vbox3">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <child internal-child="action_area">
-          <object class="GtkHButtonBox" id="dialog-action_area3">
+          <object class="GtkButtonBox" id="dialog-action_area3">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="layout_style">end</property>
@@ -136,7 +178,6 @@
                 <property name="label" translatable="yes">Test Disk</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
-                <property name="use_action_appearance">False</property>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -151,7 +192,6 @@
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
                 <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
               </object>
               <packing>
@@ -305,7 +345,6 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
               </object>
               <packing>
@@ -335,11 +374,11 @@
     <property name="type_hint">dialog</property>
     <property name="gravity">center</property>
     <child internal-child="vbox">
-      <object class="GtkVBox" id="dialog-vbox10">
+      <object class="GtkBox" id="dialog-vbox10">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <child internal-child="action_area">
-          <object class="GtkHButtonBox" id="dialog-action_area10">
+          <object class="GtkButtonBox" id="dialog-action_area10">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="layout_style">end</property>
@@ -350,7 +389,6 @@
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
                 <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
               </object>
               <packing>
@@ -366,7 +404,6 @@
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
                 <property name="receives_default">False</property>
-                <property name="use_action_appearance">False</property>
                 <property name="use_stock">True</property>
                 <signal name="clicked" handler="quit" swapped="no"/>
               </object>
@@ -509,18 +546,20 @@
                 </child>
                 <child>
                   <object class="GtkScrolledWindow" id="scrolledwindow1">
+                    <property name="height_request">70</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="hscrollbar_policy">never</property>
-                    <property name="vscrollbar_policy">automatic</property>
                     <property name="shadow_type">in</property>
-                    <property name="height_request">70</property>
                     <child>
                       <object class="GtkTreeView" id="source_treeview">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="headers_clickable">False</property>
                         <property name="rubber_banding">True</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection" id="treeview-selection1"/>
+                        </child>
                       </object>
                     </child>
                   </object>
@@ -551,7 +590,6 @@
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">True</property>
-                        <property name="use_action_appearance">False</property>
                         <signal name="clicked" handler="add_file_source_dialog" swapped="no"/>
                       </object>
                       <packing>
@@ -607,17 +645,19 @@
                 </child>
                 <child>
                   <object class="GtkScrolledWindow" id="scrolledwindow2">
+                    <property name="height_request">70</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="hscrollbar_policy">never</property>
-                    <property name="vscrollbar_policy">automatic</property>
                     <property name="shadow_type">in</property>
-                    <property name="height_request">70</property>
                     <child>
                       <object class="GtkTreeView" id="dest_treeview">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="headers_clickable">False</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection" id="treeview-selection2"/>
+                        </child>
                       </object>
                     </child>
                   </object>
@@ -659,7 +699,6 @@
                         <property name="label">gtk-open</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">True</property>
-                        <property name="use_action_appearance">False</property>
                         <property name="use_stock">True</property>
                         <signal name="clicked" handler="open_dest_folder" swapped="no"/>
                       </object>
@@ -674,7 +713,6 @@
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">True</property>
-                        <property name="use_action_appearance">False</property>
                         <signal name="clicked" handler="format_dest_clicked" swapped="no"/>
                         <child>
                           <object class="GtkHBox" id="hbox3">
@@ -767,7 +805,7 @@
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
-                                <property name="use_action_appearance">False</property>
+                                <property name="xalign">0.5</property>
                                 <property name="active">True</property>
                                 <property name="draw_indicator">True</property>
                               </object>
@@ -837,7 +875,7 @@
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="receives_default">False</property>
-                            <property name="use_action_appearance">False</property>
+                            <property name="xalign">0.5</property>
                             <property name="active">True</property>
                             <property name="draw_indicator">True</property>
                             <property name="group">persist_enabled</property>
@@ -877,7 +915,6 @@
                     <property name="can_focus">True</property>
                     <property name="receives_default">True</property>
                     <property name="border_width">4</property>
-                    <property name="use_action_appearance">False</property>
                     <property name="use_stock">True</property>
                   </object>
                   <packing>
@@ -894,7 +931,6 @@
                     <property name="can_focus">True</property>
                     <property name="receives_default">True</property>
                     <property name="border_width">4</property>
-                    <property name="use_action_appearance">False</property>
                     <property name="use_stock">True</property>
                     <signal name="clicked" handler="quit" swapped="no"/>
                   </object>
@@ -914,7 +950,6 @@
                     <property name="has_default">True</property>
                     <property name="receives_default">True</property>
                     <property name="border_width">4</property>
-                    <property name="use_action_appearance">False</property>
                     <signal name="clicked" handler="install" swapped="no"/>
                   </object>
                   <packing>

=== modified file 'setup.py'
--- setup.py	2012-01-21 13:31:38 +0000
+++ setup.py	2013-01-24 17:05:29 +0000
@@ -20,11 +20,13 @@
               'usbcreator.frontends.base',
               'usbcreator.backends',
               'usbcreator.backends.base',
+              'usbcreator.backends.fastboot',
               'usbcreator.backends.udisks',
              ],
     scripts=['bin/usb-creator-gtk','bin/usb-creator-kde'],
     data_files=[('share/usb-creator', ['gui/usbcreator-gtk.ui']),
                 ('share/usb-creator', ['bin/usb-creator-helper']),
+                ('share/usb-creator', ['gui/ubuntu-nexus7-USAGE-NOTICE-en.txt']),
                 ('share/icons/hicolor/scalable/apps', ['desktop/usb-creator-gtk.svg', 'desktop/usb-creator-kde.svg']),
                 ('share/kde4/apps/usb-creator-kde', ['gui/usbcreator-kde.ui']),
                 ('/etc/dbus-1/system.d', ['dbus/com.ubuntu.USBCreator.conf']),

=== modified file 'usbcreator/backends/base/backend.py'
--- usbcreator/backends/base/backend.py	2012-11-09 00:42:18 +0000
+++ usbcreator/backends/base/backend.py	2013-01-24 17:05:29 +0000
@@ -75,6 +75,19 @@
     def get_current_source(self):
         return self.current_source
 
+    # Common helpers
+
+    def _device_removed(self, device):
+        logging.debug('Device has been removed from the system: %s' % device)
+        if device in self.sources:
+            if misc.callable(self.source_removed_cb):
+                self.source_removed_cb(device)
+            self.sources.pop(device)
+        elif device in self.targets:
+            if misc.callable(self.target_removed_cb):
+                self.target_removed_cb(device)
+            self.targets.pop(device)
+    
     # Signals.
 
     def source_added_cb(self, drive):
@@ -170,11 +183,13 @@
         return changed
 
     def install(self, source, target, persist, device=None,
-                allow_system_internal=False):
+                allow_system_internal=False,
+                fastboot_mode=False):
         logging.debug('Starting install thread.')
         self.install_thread = usbcreator.install.install(
             source, target, persist, device=device,
-            allow_system_internal=allow_system_internal)
+            allow_system_internal=allow_system_internal,
+            fastboot_mode=fastboot_mode)
         # Connect signals.
         self.install_thread.success = self.success_cb
         self.install_thread.failure = self.failure_cb

=== added directory 'usbcreator/backends/fastboot'
=== added file 'usbcreator/backends/fastboot/__init__.py'
--- usbcreator/backends/fastboot/__init__.py	1970-01-01 00:00:00 +0000
+++ usbcreator/backends/fastboot/__init__.py	2013-01-24 17:05:29 +0000
@@ -0,0 +1,1 @@
+from usbcreator.backends.fastboot.backend import FastbootBackend

=== added file 'usbcreator/backends/fastboot/backend.py'
--- usbcreator/backends/fastboot/backend.py	1970-01-01 00:00:00 +0000
+++ usbcreator/backends/fastboot/backend.py	2013-01-24 17:05:29 +0000
@@ -0,0 +1,68 @@
+import logging
+
+from gi.repository import GUdev
+from gi.repository import GLib
+
+from usbcreator.backends.base import Backend
+from usbcreator import misc
+
+KNOWN_IDS = {
+    'ID_VENDOR_ID': ('18d1',),
+    'ID_MODEL_ID': ('4e40', 'd001',),
+}
+
+class FastbootBackend(Backend):
+    def __init__(self):
+        Backend.__init__(self)
+        logging.debug('FastbootBackend')
+        self.client = GUdev.Client(subsystems=['usb'])
+        
+    def on_uevent(self, action, device):
+        logging.debug('action: %s' % action)
+        logging.debug('device: %s' % device.get_sysfs_path())
+
+        for key,ids in KNOWN_IDS.items():
+            result = device.get_property(key)
+            if result not in ids:
+                logging.debug('Unknown %s: %s' % (key, result))
+                return
+            
+        [logging.debug('%s=%s' % (k, device.get_property(k))) for k in device.get_property_keys()]
+        
+        key = device.get_property('ID_SERIAL_SHORT')
+        
+        if action == 'add':
+            self.targets[key] = {
+                'vendor'     : device.get_property('ID_VENDOR_FROM_DATABASE'),
+                'model'      : device.get_property('ID_MODEL'),
+                'label'      : '',
+                'device'     : device.get_property('ID_SERIAL_SHORT'),
+                'status'     : misc.CAN_USE,
+                }
+            if misc.callable(self.target_added_cb):
+                self.target_added_cb(key)
+        elif action == 'remove':
+            self._device_removed(key)
+            
+    def detect_devices(self):
+        def _on_uevent(client, action, device):
+            self.on_uevent(action, device)
+            
+        self.client.connect('uevent', _on_uevent)
+        # Just in case, go over already attached devices
+        for device in self.client.query_by_subsystem('usb'):
+            self.on_uevent('add', device)
+
+    def update_free(self):
+        # No progress yet.
+        return True
+
+    def _is_casper_cd(self, filename):
+        # no cds for us
+        return None    
+
+    def unmount(self):
+        return
+
+    def shutdown(self):
+        return

=== modified file 'usbcreator/backends/udisks/backend.py'
--- usbcreator/backends/udisks/backend.py	2012-07-06 12:49:06 +0000
+++ usbcreator/backends/udisks/backend.py	2013-01-24 17:05:29 +0000
@@ -229,17 +229,6 @@
         else:
             logging.debug('not adding device: 0 byte disk.')
 
-    def _device_removed(self, device):
-        logging.debug('Device has been removed from the system: %s' % device)
-        if device in self.sources:
-            if misc.callable(self.source_removed_cb):
-                self.source_removed_cb(device)
-            self.sources.pop(device)
-        elif device in self.targets:
-            if misc.callable(self.target_removed_cb):
-                self.target_removed_cb(device)
-            self.targets.pop(device)
-
     # Device manipulation functions.
     def _is_casper_cd(self, filename):
         cmd = ['isoinfo', '-J', '-i', filename, '-x', '/.disk/info']

=== modified file 'usbcreator/frontends/gtk/frontend.py'
--- usbcreator/frontends/gtk/frontend.py	2013-01-10 14:58:41 +0000
+++ usbcreator/frontends/gtk/frontend.py	2013-01-24 17:05:29 +0000
@@ -30,8 +30,10 @@
 
 if 'USBCREATOR_LOCAL' in os.environ:
     ui_path = os.path.join(os.getcwd(), 'gui/usbcreator-gtk.ui')
+    eula_path = os.path.join(os.getcwd(), 'gui/ubuntu-nexus7-USAGE-NOTICE-en.txt')
 else:
     ui_path = '/usr/share/usb-creator/usbcreator-gtk.ui'
+    eula_path = '/usr/share/usb-creator/ubuntu-nexus7-USAGE-NOTICE-en.txt'
 
 GObject.threads_init()
 Gdk.threads_init()
@@ -60,10 +62,12 @@
         DBusGMainLoop(set_as_default=True)
 
     def __init__(self, backend, img=None, persistent=True,
-                 allow_system_internal=False):
+                 allow_system_internal=False,
+                 fastboot_mode=False):
 
         self.allow_system_internal = allow_system_internal
-
+        self.fastboot_mode = fastboot_mode
+        
         self.all_widgets = set()
 
         self.builder = Gtk.Builder()
@@ -98,11 +102,6 @@
         self.finished_exit.connect('clicked', lambda x: self.finished_dialog.hide())
         self.failed_exit.connect('clicked', lambda x: self.failed_exit.hide())
         self.progress_cancel_button.connect('clicked', lambda x: self.warning_dialog.show())
-        # we currently do not have help
-        self.button_help.hide()
-        #self.button_help.connect('clicked', lambda x: Gtk.show_uri(self.button_help.get_screen(),
-        #                                                           'ghelp:usb-creator',
-        #                                                           Gtk.get_current_event_time()))
 
         def format_value(scale, value):
             return misc.format_mb_size(value)
@@ -129,6 +128,30 @@
         # Pulse state.
         self.pulsing = False
 
+        # Hide erase in fastboot mode
+        if self.fastboot_mode:
+            self.format_dest.hide()
+            self.intro_label.set_text(_('To run Ubuntu from a portable device, it needs to be set up first.'))
+            self.label2.set_text(_('Source disc image (.img):'))
+            self.label3.set_text(_('Target device:'))
+            self.window.set_title(_('Ubuntu Core Installer'))
+            self.button_install.set_label(_('Install Ubuntu Core'))
+            self.button_help.set_label(_('Legal'))
+            self.button_help.connect('clicked', self.show_eula)
+            self.eula_dialog.add_buttons(
+                Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+                Gtk.STOCK_OK, Gtk.ResponseType.OK
+            )
+            with open(eula_path, 'r') as f:
+                text = ''.join(f.readlines())
+            self.eula_text.get_buffer().set_text(text)
+        else:
+            # we currently do not have help
+            self.button_help.hide()
+            #self.button_help.connect('clicked', lambda x: Gtk.show_uri(self.button_help.get_screen(),
+            #                                                           'ghelp:usb-creator',
+            #                                                           Gtk.get_current_event_time()))
+            
         self.setup_sources_treeview()
         self.setup_targets_treeview()
         self.persist_vbox.set_sensitive(False)
@@ -345,6 +368,8 @@
         cell_name.set_property('ellipsize', Pango.EllipsizeMode.END)
         cell_pixbuf = Gtk.CellRendererPixbuf()
         column_name = Gtk.TreeViewColumn(_('CD-Drive/Image'))
+        if self.fastboot_mode:
+            column_name = Gtk.TreeViewColumn(_('Image'))
         column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
         column_name.set_resizable(True)
         column_name.set_expand(True)
@@ -355,15 +380,16 @@
         column_name.set_cell_data_func(cell_name, column_data_func, 0)
         column_name.set_cell_data_func(cell_pixbuf, pixbuf_data_func, None)
 
-        cell_version = Gtk.CellRendererText()
-        cell_version.set_property('ellipsize', Pango.EllipsizeMode.END)
-        column_name = Gtk.TreeViewColumn(_('OS Version'), cell_version)
-        column_name.set_cell_data_func(cell_version, column_data_func, 1)
-        column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
-        column_name.set_resizable(True)
-        column_name.set_expand(True)
-        column_name.set_min_width(75)
-        self.source_treeview.append_column(column_name)
+        if not self.fastboot_mode:
+            cell_version = Gtk.CellRendererText()
+            cell_version.set_property('ellipsize', Pango.EllipsizeMode.END)
+            column_name = Gtk.TreeViewColumn(_('OS Version'), cell_version)
+            column_name.set_cell_data_func(cell_version, column_data_func, 1)
+            column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+            column_name.set_resizable(True)
+            column_name.set_expand(True)
+            column_name.set_min_width(75)
+            self.source_treeview.append_column(column_name)
 
         cell_size = Gtk.CellRendererText()
         cell_size.set_property('ellipsize', Pango.EllipsizeMode.END)
@@ -416,16 +442,20 @@
                 m.row_changed(m.get_path(iterator), iterator)
                 break
             iterator = m.iter_next(iterator)
+
+        target = self.backend.targets[udi]
+
         # Update persistence maximum value.
-        self.persist_vbox.set_sensitive(False)
-        self.persist_enabled_vbox.set_sensitive(False)
-        target = self.backend.targets[udi]
-        persist_mb = target['persist'] / 1024 / 1024
-        if persist_mb > misc.MIN_PERSISTENCE:
-                self.persist_vbox.set_sensitive(True)
-                self.persist_enabled_vbox.set_sensitive(True)
-                self.persist_value.set_range(misc.MIN_PERSISTENCE, persist_mb)
-                self.persist_value.set_value(misc.MIN_PERSISTENCE)
+        if not self.fastboot_mode:
+            self.persist_vbox.set_sensitive(False)
+            self.persist_enabled_vbox.set_sensitive(False)
+            persist_mb = target['persist'] / 1024 / 1024
+            if persist_mb > misc.MIN_PERSISTENCE:
+                    self.persist_vbox.set_sensitive(True)
+                    self.persist_enabled_vbox.set_sensitive(True)
+                    self.persist_value.set_range(misc.MIN_PERSISTENCE, persist_mb)
+                    self.persist_value.set_value(misc.MIN_PERSISTENCE)
+
         # Update install button state.
         status = target['status']
         source = self.backend.get_current_source()
@@ -467,6 +497,9 @@
         if udi:
             self.update_target(udi)
 
+        if self.fastboot_mode:
+            return
+
         dev = self.backend.targets[udi]
         p = dev['parent']
         if p and p in self.backend.targets:
@@ -546,45 +579,51 @@
         column_name.set_cell_data_func(cell_name, column_data_func, 0)
         column_name.set_cell_data_func(cell_pixbuf, pixbuf_data_func, None)
 
-        cell_name = Gtk.CellRendererText()
-        cell_name.set_property('ellipsize', Pango.EllipsizeMode.END)
-        column_name = Gtk.TreeViewColumn(_('Label'), cell_name)
-        column_name.set_cell_data_func(cell_name, column_data_func, 1)
-        column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
-        column_name.set_resizable(True)
-        column_name.set_expand(True)
-        column_name.set_min_width(75)
-        self.dest_treeview.append_column(column_name)
-
-        cell_capacity = Gtk.CellRendererText()
-        cell_capacity.set_property('ellipsize', Pango.EllipsizeMode.END)
-        column_name = Gtk.TreeViewColumn(_('Capacity'), cell_capacity)
-        column_name.set_cell_data_func(cell_capacity, column_data_func, 2)
-        column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
-        column_name.set_resizable(True)
-        column_name.set_expand(False)
-        column_name.set_min_width(75)
-        self.dest_treeview.append_column(column_name)
-
-        cell_free = Gtk.CellRendererText()
-        cell_free.set_property('ellipsize', Pango.EllipsizeMode.END)
-        column_name = Gtk.TreeViewColumn(_('Free Space'), cell_free)
-        column_name.set_cell_data_func(cell_free, column_data_func, 3)
-        column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
-        column_name.set_resizable(True)
-        column_name.set_expand(False)
-        column_name.set_min_width(75)
-        self.dest_treeview.append_column(column_name)
+        if not self.fastboot_mode:
+            cell_name = Gtk.CellRendererText()
+            cell_name.set_property('ellipsize', Pango.EllipsizeMode.END)
+            column_name = Gtk.TreeViewColumn(_('Label'), cell_name)
+            column_name.set_cell_data_func(cell_name, column_data_func, 1)
+            column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+            column_name.set_resizable(True)
+            column_name.set_expand(True)
+            column_name.set_min_width(75)
+            self.dest_treeview.append_column(column_name)
+
+            cell_capacity = Gtk.CellRendererText()
+            cell_capacity.set_property('ellipsize', Pango.EllipsizeMode.END)
+            column_name = Gtk.TreeViewColumn(_('Capacity'), cell_capacity)
+            column_name.set_cell_data_func(cell_capacity, column_data_func, 2)
+            column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+            column_name.set_resizable(True)
+            column_name.set_expand(False)
+            column_name.set_min_width(75)
+            self.dest_treeview.append_column(column_name)
+
+            cell_free = Gtk.CellRendererText()
+            cell_free.set_property('ellipsize', Pango.EllipsizeMode.END)
+            column_name = Gtk.TreeViewColumn(_('Free Space'), cell_free)
+            column_name.set_cell_data_func(cell_free, column_data_func, 3)
+            column_name.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
+            column_name.set_resizable(True)
+            column_name.set_expand(False)
+            column_name.set_min_width(75)
+            self.dest_treeview.append_column(column_name)
 
     def add_file_source_dialog(self, *args):
         filename = ''
         chooser = Gtk.FileChooserDialog(title=None,action=Gtk.FileChooserAction.OPEN,
             buttons=(Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN,Gtk.ResponseType.OK))
-        for p, n in (('*.iso', _('CD Images')), ('*.img', _('Disk Images'))):
+        def _add_filter(p, n):
             filter = Gtk.FileFilter()
             filter.add_pattern(p)
             filter.set_name(n)
             chooser.add_filter(filter)
+
+        if not self.fastboot_mode:
+            _add_filter('*.iso', _('CD Images'))
+        _add_filter('*.img', _('Disk Images'))            
+
         folder = os.path.expanduser('~')
         chooser.set_current_folder(folder)
         response = chooser.run()
@@ -599,6 +638,9 @@
         source = self.get_source()
         target = self.get_target()
         persist = self.get_persistence()
+        if self.fastboot_mode and not misc.check_eula():    
+            if self.show_eula() == Gtk.ResponseType.CANCEL:
+                return
         # TODO evand 2009-07-31: Make these the default values in the
         # GtkBuilder file.
         starting_up = _('Starting up...')
@@ -610,8 +652,10 @@
             self.unity.show_progress()
             self.delete_timeout(self.update_loop)
             try:
-                self.backend.install(source, target, persist,
-                                     allow_system_internal=self.allow_system_internal)
+                self.backend.install(
+                    source, target, persist,
+                    allow_system_internal=self.allow_system_internal,
+                    fastboot_mode=self.fastboot_mode)
             except:
                 self._fail()
 
@@ -663,6 +707,12 @@
         retry_dialog.destroy()
         return response == Gtk.ResponseType.YES
 
+    def show_eula(self, unused=None):
+        response = self.eula_dialog.run()
+        if response == Gtk.ResponseType.OK:
+            misc.check_eula(True)
+        self.eula_dialog.hide()
+        return response
 
     def quit(self, *args):
         self.backend.cancel_install()
@@ -706,6 +756,10 @@
         except dbus.DBusException:
             logging.exception('Error checking for kvm:')
 
+        if self.fastboot_mode:
+            self.finished_dialog_label.set_text(
+                _('Installation is complete. Your device is rebooting into Ubuntu Core.'))
+            self.kvm_test.hide()
         self.finished_dialog.run()
         self.backend.shutdown()
         Gtk.main_quit()

=== modified file 'usbcreator/install.py'
--- usbcreator/install.py	2012-05-04 10:35:51 +0000
+++ usbcreator/install.py	2013-01-24 17:05:29 +0000
@@ -26,6 +26,7 @@
 from threading import Thread, Event
 import logging
 from hashlib import md5
+import time
 
 if sys.platform != 'win32':
     from usbcreator.misc import MAX_DBUS_TIMEOUT
@@ -65,13 +66,15 @@
 
 class install(Thread):
     def __init__(self, source, target, persist, device=None,
-                 allow_system_internal=False):
+                 allow_system_internal=False,
+                 fastboot_mode=False):
         Thread.__init__(self)
         self.source = source
         self.target = target
         self.persist = persist
         self.device = device
         self.allow_system_internal = allow_system_internal
+        self.fastboot_mode = fastboot_mode
         self._stopevent = Event()
         self.progress_thread = None
         logging.debug('install thread source: %s' % source)
@@ -137,7 +140,13 @@
                 if ext not in ['.iso', '.img']:
                     self._failure(_('The extension "%s" is not supported.') %
                                     ext)
-                if ext == '.iso':
+                if self.fastboot_mode:
+                    self.bootimg = os.path.splitext(self.source)[0] + '.bootimg'
+                    if not os.path.isfile(self.bootimg):
+                        self._failure(_('Missing matching "%s" for source image %s.') %
+                                    (self.bootimg, self.source))
+                    self.fastboot_install()
+                elif ext == '.iso':
                     if sys.platform == 'win32':
                         self.cdimage_install()
                     else:
@@ -394,6 +403,31 @@
             except dbus.DBusException:
                 self._failure(failure_msg)
 
+    def _fastboot_command(self, cmd):
+        fastboot_cmd = ['fastboot', '-s', self.target]
+        fastboot_cmd.extend(cmd)
+        popen(fastboot_cmd)
+
+    def fastboot_install(self):
+        self.progress(0, 3*60+9, True)
+        self.progress_message(_('Erasing boot partition...'))
+        self._fastboot_command(['erase', 'boot'])
+        self.progress(5, 3*60+7, True)
+        self.progress_message(_('Erasing user partition...'))
+        self._fastboot_command(['erase', 'userdata'])
+        self.progress(10, 3*60+5, True)
+        self.progress_message(_('Flashing boot partition...'))
+        self._fastboot_command(['flash', 'boot', self.bootimg])
+        self.progress(15, 3*60+3, True)
+        self.progress_message(_('Flashing user partition...'))
+        self.progress_pulse()
+        self._fastboot_command(['flash', 'userdata', self.source])
+        self.progress_pulse_stop()        
+        self.progress(95, 1, True)
+        self.progress_message(_('Rebooting device...'))
+        self._fastboot_command(['reboot'])
+        self.progress(100, 0, True)
+
     def cdimage_install(self):
         # Build.
 

=== modified file 'usbcreator/misc.py'
--- usbcreator/misc.py	2012-11-09 01:23:47 +0000
+++ usbcreator/misc.py	2013-01-24 17:05:29 +0000
@@ -35,6 +35,8 @@
 MAX_LOG_SIZE = 1024 * 1024 * 1
 MAX_LOG_BACKUP = 0
 
+EULA_STAMP = '.ubuntu-nexus7-installer.notice-accepted'
+
 if sys.platform != 'win32':
     # TODO xnox 20121109 should not hard-code timeouts, instead should
     # do async dbus calls with a qt or glib main loop running.
@@ -78,6 +80,13 @@
         log.addHandler(handler)
     log.setLevel(logging.DEBUG)
 
+def check_eula(save=False):
+    eula_stamp = os.path.expanduser('~/' + EULA_STAMP)
+    if save:
+        with open(eula_stamp, 'w') as f:
+            pass
+    return os.path.isfile(eula_stamp)
+    
 def format_size(size):
     """Format a partition size."""
     # Taken from ubiquity's ubiquity/misc.py