3 Copyright 2010 Google Inc.
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
9 http://www.apache.org/licenses/LICENSE-2.0
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
17 Original slides: Marcin Wichary (mwichary@google.com)
18 Modifications: Ernest Delgado (ernestd@google.com)
19 Alex Russell (slightlyoff@chromium.org)
22 <html manifest="cache.manifest">
25 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
29 <meta http-equiv="X-UA-Compatible" content="IE=edge;chrome=1" />
32 <meta charset="utf-8" />
33 <title>Dobrica Pavlinušić: Android from source in command-line</title>
34 <link href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans+Mono" rel="stylesheet" type="text/css" />
38 font: 20px "Lucida Grande", "Trebuchet MS", Verdana, sans-serif;
65 -webkit-transition: -webkit-transform 1s ease-in-out;
66 -moz-transition: -moz-transform 1s ease-in-out;
67 -o-transition: -o-transform 1s ease-in-out;
68 transition: transform 1s ease-in-out;
70 /* so it's visible in the iframe. */
71 -webkit-transform: scale(0.8);
72 -moz-transform: scale(0.8);
73 -o-transform: scale(0.8);
74 transform: scale(0.8);
87 background-color: #eee;
88 background: -webkit-gradient(linear, left bottom, left top, from(#bbd), to(#fff));
89 background: -moz-linear-gradient(bottom, #bbd, #fff);
90 background: linear-gradient(bottom, #bbd, #fff);
91 -webkit-transition: all 0.25s ease-in-out;
92 -moz-transition: all 0.25s ease-in-out;
93 -o-transition: all 0.25s ease-in-out;
94 transition: all 0.25s ease-in-out;
95 -webkit-transform: scale(1);
96 -moz-transform: scale(1);
97 -o-transform: scale(1);
101 .slide:nth-child(even) {
102 -moz-border-radius: 20px 0;
103 -khtml-border-radius: 20px 0;
104 border-radius: 20px 0; /* includes Opera 10.5+ */
105 -webkit-border-top-left-radius: 20px;
106 -webkit-border-bottom-right-radius: 20px;
109 .slide:nth-child(odd) {
110 -moz-border-radius: 0 20px;
111 -khtml-border-radius: 0 20px;
112 border-radius: 0 20px;
113 -webkit-border-top-right-radius: 20px;
114 -webkit-border-bottom-left-radius: 20px;
117 .slide p, .slide textarea {
130 .slide.title > .counter,
131 .slide.segue > .counter,
132 .slide.mainTitle > .counter {
143 margin-left: -2400px;
149 margin-left: -1400px;
169 body.three-d div.slides {
170 -webkit-transform: translateX(50px) scale(0.8) rotateY(10deg);
171 -moz-transform: translateX(50px) scale(0.8) rotateY(10deg);
172 -o-transform: translateX(50px) scale(0.8) rotateY(10deg);
173 transform: translateX(50px) scale(0.8) rotateY(10deg);
178 @font-face { font-family: 'Junction'; src: url(src/Junction02.otf); }
179 @font-face { font-family: 'LeagueGothic'; src: url(src/LeagueGothic.otf); }
182 font-family: 'Droid Sans';
184 letter-spacing: -.05em;
185 text-shadow: rgba(0, 0, 0, 0.2) 0 2px 5px;
202 font-family: 'Droid Sans';
214 font-family: 'Droid Sans';
216 text-shadow: rgba(0, 0, 0, 0.2) 0 2px 5px;
217 margin: 100px 30px 0;
224 margin: 20px 0 0 30px;
229 display: inline-block;
230 text-decoration: none;
232 border-bottom: 2px solid #3f3f3f;
270 vertical-align: middle;
277 font-family: 'Droid Sans Mono', Courier;
280 background: rgba(255, 0, 0, 0.05);
281 -webkit-border-radius: 8px;
282 -khtml-border-radius: 8px;
283 -moz-border-radius: 8px;
285 border: 1px solid rgba(255, 0, 0, 0.2);
289 font-family: Monaco, Courier;
290 border: 1px solid #c61800;
296 font-family: Helvetica;
299 input[type="range"] {
304 margin: 20px 10px 0 0;
305 font-family: Verdana;
315 text-shadow: #c61800 0 0 1px;
316 /*letter-spacing: -1px;*/
322 text-shadow: #18a600 0 0 1px;
324 pre input[type="range"] {
334 background: rgba(255, 255, 255, 0.4);
335 -webkit-border-radius: 8px;
336 -khtml-border-radius: 8px;
337 -moz-border-radius: 8px;
340 border: 1px solid rgba(0, 0, 0, 0.2);
344 -moz-border-radius: 8px;
345 -khtml-border-radius: 8px;
346 -webkit-border-radius: 8px;
348 border: 1px solid rgba(0, 0, 0, 0.2);
352 font-family: 'Droid Sans';
354 display: inline-block;
355 padding: 6px 10px 3px 10px;
362 -moz-border-radius: 10px;
363 -khtml-border-radius: 10px;
364 -webkit-border-radius: 10px;
367 box-shadow: rgba(0, 0, 0, 0.1) 0 2px 5px;
368 -webkit-box-shadow: rgba(0, 0, 0, 0.1) 0 2px 5px;
369 -moz-box-shadow: rgba(0, 0, 0, 0.1) 0 2px 5px;
370 -o-box-shadow: rgba(0, 0, 0, 0.1) 0 2px 5px;
373 .key { font-family: Arial; }
375 :not(header) > .key {
381 -webkit-column-count: 2;
382 -moz-column-count: 2;
387 -webkit-text-stroke-color: red;
388 -webkit-text-stroke-width: 1px;
389 } /* currently webkit-only */
395 #presentation-counter {
406 div:not(.current).reduced {
407 -webkit-transform: scale(0.8);
408 -moz-transform: scale(0.8);
409 -o-transform: scale(0.8);
410 transform: scale(0.8);
414 -webkit-transition: none;
415 -moz-transition: none;
422 background-color: #fff;
433 <div class="presentation">
434 <div id="presentation-counter"></div>
450 Binary blobs from phone using adb pull
452 Downloads somewhat working (EeePC 701)
453 USB networking broken
454 http://github.com/dpavlin/android-adb-usb-tether
456 Debug from command-line
457 Do we need FLOSS app market?
462 <section class="middle">
463 <h2>Android from source in command-line</h2>
464 <img src="android-fastboot.png">
467 <br><a href="http://blog.rot13.org">http://blog.rot13.org/</a>
477 <h2>Compile Android from source code</h2>
478 <h2>Command-line usage</h2>
480 <h2>Do we need FLOSS app market?</h2>
486 <section class="middle">
494 <h1>Compile Android from source</h1>
497 <h2>Why is source important?</h2>
499 Lookup how is something implemented
500 - <a href="http://github.com/dpavlin/android-adb-usb-tether">and find out why usb networking doesn't work for Google apps</a>
502 <p>Lastest Android released on abandoned hardware (Froyo on G1)</p>
504 <h2>Ability to install your own ROM</h2>
506 severly udermined by attempts to provide full signature security from hardware to userspace
510 <span class="key">></span>
512 <span class="key">></span>
516 <h2>Porting to other devices</h2>
523 <h1>Compile Android from source</h1>
528 <ul class="bulleted">
529 <li>only for <b>rooted</b> phones (targets developers)
530 <li>binary blobs from phone using <tt>adb pull</tt></li>
531 <li><a href="http://wiki.cyanogenmod.com/index.php?title=Compile_CyanogenMod_for_Dream_%26_Sapphire">compile for your phone</a></li>
533 dpavlin@android:/srv/cyanogen$ <b>. build/envsetup.sh</b>
534 dpavlin@android:/srv/cyanogen$ <b>lunch</b>
535 dpavlin@android:/srv/cyanogen$ <b>make -j8</b>
537 <li>fastboot or sdcard <tt>update.zip</tt> flashing
538 <li><a href="http://github.com/CyanogenMod/android_vendor_cyanogen/blob/froyo/CHANGELOG.mkdn">additional features</a> on top of Google release
547 <h1>Compile Android from source</h1>
552 <ul class="bulleted">
553 <li>Latest release <a href="http://android-x86.googlecode.com/files/android-x86-1.6-r2.iso">1.6r2</a> works on EeePC 701, VrtualBox and kvm</li>
554 <li><a href="http://www.android-x86.org/getsourcecode">compile source</a></li>
555 <li>Froyo (2.2) and Eclair (2.1) in various stages of <em>working</em>
556 <li>networking in kvm: boot in <b>debug</b> mode and in first prompt load network module:
558 # <b>modprobe 8139cp</b>
560 <li>add <tt>vga=788</tt> to kernel line in <tt>grub</tt> to make kvm happy
574 dpavlin@x200:/virtual/android$ <b>adb shell</b>
576 Linux localhost 2.6.35.4-cyanogenmod #57 PREEMPT Fri Sep 10 00:11:46 UTC 2010 armv6l GNU/Linux
580 <p>Everything your Android is storing into log, <b>multiple buffers!</b></p>
582 dpavlin@x200:/virtual/android$ <b>adb logcat -b events</b>
585 <h2>multiple devices</h2>
587 dpavlin@x200:~$ <b>export ANDROID_SERIAL=127.0.0.1:5555</b>
595 <section class="middle">
596 <h1>Android userland</h1>
598 <a href="http://github.com/dpavlin/android-command-line">http://github.com/dpavlin/android-command-line</a>
606 <h1>Dump debugging informations</h1>
609 <p>Big and verbose state of your device</p>
611 dpavlin@x200:~$ <b>adb shell dumpstate > dumpstate.txt</b>
616 <p>Who is listening to those intents?</p>
618 dpavlin@x200:~$ <b>adb shell dumpsys > dumpsys.txt
629 <h2>Make phone calls from command line</h2>
631 # <b>service call phone 2 s16 "+31611530555"</b>
635 Where did <b>2</b> came from?
636 <a href="http://www.androidjavadoc.com/1.0_r1_src/constant-values.html#com.android">com.android.internal.telephony.ITelephony.Stub</a>
639 <h2>Rotate screen in emulator</h2>
641 $ <b>service call window 18 i32 1</b> # landscape
642 $ <b>service call window 18 i32 0</b> # portrait
655 # <b>am start -a android.intent.action.MAIN -n com.android.browser/.BrowserActivity</b>
660 # <b>am start -a android.intent.action.MAIN -n com.android.settings/.Settings</b>
673 Install application from phone to other device
677 dpavlin@x200:~$ <b>adb shell pm list packages | grep replica</b>
678 package:com.replica.replicaisland
679 dpavlin@x200:~$ <b>adb shell pm path com.replica.replicaisland</b>
680 package:/mnt/asec/com.replica.replicaisland-1/pkg.apk
681 dpavlin@x200:~$ <b>adb pull /mnt/asec/com.replica.replicaisland-1/pkg.apk</b>
682 1373 KB/s (5144485 bytes in 3.658s)
683 dpavlin@x200:~$ <b>ANDROID_SERIAL=192.168.1.32:5555 adb install pkg.apk</b>
684 779 KB/s (5144485 bytes in 6.446s)
685 pkg: /data/local/tmp/pkg.apk
694 <h1>Do we need FLOSS app market?</h1>
697 <a href="http://lwn.net/Articles/373128/">Jonathan Corbet of LWM fame</a> think we do and I agree.
699 <h2>Working idea</h2>
701 <ul class="bulleted">
702 <li>FLOSS licence</li>
703 <li>source code repository (git)</li>
704 <li>all source searchable (gitweb?)</li>
705 <li>automatic builds (x86 NDK?)</li>
706 <li><b>download links from web!</b></li>
707 <li>backup apps to cloud (CouchDB?)</li>
710 <p>Anybody interested in something like that?</p>
716 <!-- extra slides -->
719 <section class="middle">
720 <h2>The Difference Between Android And iPhone</h2>
721 <p><a href="http://www.androidpolice.com/2010/09/16/comic-the-difference-between-android-and-iphone-this-pretty-much-sums-it-up/">This Pretty Much Sums It Up</a>
726 <section class="middle">
728 <img src="commic-Android.png">
733 <section class="middle">
735 <img src="commic-iPhone.png">
740 <section class="middle">
741 <h1>Questions? Comments?</h1>
742 <h2>More information</h2>
743 <p>this presentation: <a href="http://dpavlin.github.com/android-command-line/">http://dpavlin.github.com/android-command-line/</a></p>
744 <p>repository: <a href="http://github.com/dpavlin/android-command-line/">http://github.com/dpavlin/android-command-line/</a></p>
745 <p>my blog: <a href="http://blog.rot13.org">http://dpavlin.github.com/android-command-line/</a></p>
751 <section class="middle">
756 <section class="middle">
757 <h2>Slides template</h2>
758 <p>Click here, Press <span class="key">→</span> key to advance.</p>
764 <section class="middle">
769 <li>[arrow keys] to go next and previous</p></li>
770 <li>[mouse scroll wheel] to go next and previous</p></li>
771 <li>[Ctrl or Command] + [+/-] to zoom in and out</p></li>
772 <li>[touch gestures] for mobile devices</p></li>
778 <section class="middle">
783 <li><input type="checkbox" id="toggle-size" /><label for="toggle-size"><span>Grow</span></label></li>
784 <li><input type="checkbox" id="toggle-transitions" checked /><label for="toggle-transitions"><span>Transitions</span></label></li>
785 <li><input type="checkbox" id="toggle-gradients" checked /><label for="toggle-gradients"><span>Gradients</span></label></li>
786 <li><input type="checkbox" id="toggle-counter" checked /><label for="toggle-counter"><span>Slide numbers</span></label></li>
796 <h2>Placeholder content</h2>
798 <pre>// some sample code
799 <canvas id="canvas" width="838" height="220"></canvas>
802 var canvasContext = document.getElementById("canvas").<b>getContext</b>("2d");
803 canvasContext.<b>fillRect</b>(250, 25, 150, 100);
805 canvasContext.<b>beginPath</b>();
806 canvasContext.<b>arc</b>(450, 110, 100, Math.PI * 1/2, Math.PI * 3/2);
807 canvasContext.<b>lineWidth</b> = 15;
808 canvasContext.<b>lineCap</b> = 'round';
809 canvasContext.<b>strokeStyle</b> = 'rgba(255, 127, 0, 0.5)';
810 canvasContext.<b>stroke</b>();
813 <ul class="bulleted">
823 </div> <!-- slides -->
825 </div> <!-- presentation -->
830 var disableBuilds = true;
833 var spaces = /\s+/, a1 = [''];
835 var toArray = function(list) {
836 return Array.prototype.slice.call(list || [], 0);
839 var byId = function(id) {
840 if (typeof id == 'string') { return doc.getElementById(id); }
844 var query = function(query, root) {
845 if (!query) { return []; }
846 if (typeof query != 'string') { return toArray(query); }
847 if (typeof root == 'string') {
849 if(!root){ return []; }
852 root = root || document;
853 var rootIsDoc = (root.nodeType == 9);
854 var doc = rootIsDoc ? root : (root.ownerDocument || document);
856 // rewrite the query to be ID rooted
857 if (!rootIsDoc || ('>~+'.indexOf(query.charAt(0)) >= 0)) {
858 root.id = root.id || ('qUnique' + (ctr++));
859 query = '#' + root.id + ' ' + query;
861 // don't choke on something like ".yada.yada >"
862 if ('>~+'.indexOf(query.slice(-1)) >= 0) { query += ' *'; }
864 return toArray(doc.querySelectorAll(query));
867 var strToArray = function(s) {
868 if (typeof s == 'string' || s instanceof String) {
869 if (s.indexOf(' ') < 0) {
873 return s.split(spaces);
879 var addClass = function(node, classStr) {
880 classStr = strToArray(classStr);
881 var cls = ' ' + node.className + ' ';
882 for (var i = 0, len = classStr.length, c; i < len; ++i) {
884 if (c && cls.indexOf(' ' + c + ' ') < 0) {
888 node.className = cls.trim();
891 var removeClass = function(node, classStr) {
893 if (classStr !== undefined) {
894 classStr = strToArray(classStr);
895 cls = ' ' + node.className + ' ';
896 for (var i = 0, len = classStr.length; i < len; ++i) {
897 cls = cls.replace(' ' + classStr[i] + ' ', ' ');
903 if (node.className != cls) {
904 node.className = cls;
908 var toggleClass = function(node, classStr) {
909 var cls = ' ' + node.className + ' ';
910 if (cls.indexOf(' ' + classStr.trim() + ' ') >= 0) {
911 removeClass(node, classStr);
913 addClass(node, classStr);
917 var ua = navigator.userAgent;
918 var isFF = parseFloat(ua.split('Firefox/')[1]) || undefined;
919 var isWK = parseFloat(ua.split('WebKit/')[1]) || undefined;
920 var isOpera = parseFloat(ua.split('Opera/')[1]) || undefined;
922 var canTransition = (function() {
923 var ver = parseFloat(ua.split('Version/')[1]) || undefined;
924 // test to determine if this browser can handle CSS transitions.
925 var cachedCanTransition =
926 (isWK || (isFF && isFF > 3.6 ) || (isOpera && ver >= 10.5));
927 return function() { return cachedCanTransition; }
933 var Slide = function(node, idx) {
936 this._count = idx + 1;
939 addClass(this._node, 'slide distant-slide');
942 this._makeBuildList();
951 _states: [ 'distant-slide', 'far-past',
952 'past', 'current', 'future',
953 'far-future', 'distant-slide' ],
954 setState: function(state) {
955 if (typeof state != 'string') {
956 state = this._states[state];
958 if (state == 'current' && !this._visited) {
959 this._visited = true;
960 this._makeBuildList();
962 removeClass(this._node, this._states);
963 addClass(this._node, state);
964 this._currentState = state;
966 // delay first auto run. Really wish this were in CSS.
971 setTimeout(function(){ _t._runAutos(); } , 400);
973 _makeCounter: function() {
974 if(!this._count || !this._node) { return; }
975 var c = doc.createElement('span');
976 c.innerHTML = this._count;
977 c.className = 'counter';
978 this._node.appendChild(c);
980 _makeBuildList: function() {
981 this._buildList = [];
982 if (disableBuilds) { return; }
984 this._buildList = query('[data-build] > *', this._node);
986 this._buildList.forEach(function(el) {
987 addClass(el, 'to-build');
990 _runAutos: function() {
991 if (this._currentState != 'current') {
994 // find the next auto, slice it out of the list, and run it
996 this._buildList.some(function(n, i) {
997 if (n.hasAttribute('data-auto')) {
1004 var elem = this._buildList.splice(idx, 1)[0];
1005 var transitionEnd = isWK ? 'webkitTransitionEnd' : (isFF ? 'mozTransitionEnd' : 'oTransitionEnd');
1007 if (canTransition()) {
1008 var l = function(evt) {
1009 elem.parentNode.removeEventListener(transitionEnd, l, false);
1012 elem.parentNode.addEventListener(transitionEnd, l, false);
1013 removeClass(elem, 'to-build');
1015 setTimeout(function() {
1016 removeClass(elem, 'to-build');
1022 buildNext: function() {
1023 if (!this._buildList.length) {
1026 removeClass(this._buildList.shift(), 'to-build');
1034 var SlideShow = function(slides) {
1035 this._slides = (slides || []).map(function(el, idx) {
1036 return new Slide(el, idx);
1039 var h = window.location.hash;
1041 this.current = parseInt(h.split('#slide')[1], 10);
1042 }catch (e) { /* squeltch */ }
1043 this.current = isNaN(this.current) ? 1 : this.current;
1045 doc.addEventListener('keydown',
1046 function(e) { _t.handleKeys(e); }, false);
1047 doc.addEventListener('mousewheel',
1048 function(e) { _t.handleWheel(e); }, false);
1049 doc.addEventListener('DOMMouseScroll',
1050 function(e) { _t.handleWheel(e); }, false);
1051 doc.addEventListener('touchstart',
1052 function(e) { _t.handleTouchStart(e); }, false);
1053 doc.addEventListener('touchend',
1054 function(e) { _t.handleTouchEnd(e); }, false);
1055 window.addEventListener('popstate',
1056 function(e) { _t.go(e.state); }, false);
1060 SlideShow.prototype = {
1062 _update: function(dontPush) {
1063 document.querySelector('#presentation-counter').innerText = this.current;
1064 if (history.pushState) {
1066 history.pushState(this.current, 'Slide ' + this.current, '#slide' + this.current);
1069 window.location.hash = 'slide' + this.current;
1071 for (var x = this.current-1; x < this.current + 7; x++) {
1072 if (this._slides[x-4]) {
1073 this._slides[x-4].setState(Math.max(0, x-this.current));
1080 if (!this._slides[this.current-1].buildNext()) {
1081 this.current = Math.min(this.current + 1, this._slides.length);
1086 this.current = Math.max(this.current-1, 1);
1090 if (history.pushState && this.current != num) {
1091 history.replaceState(this.current, 'Slide ' + this.current, '#slide' + this.current);
1098 showNotes: function() {
1099 var isOn = this._notesOn = !this._notesOn;
1100 query('.notes').forEach(function(el) {
1101 el.style.display = (notesOn) ? 'block' : 'none';
1104 switch3D: function() {
1105 toggleClass(document.body, 'three-d');
1107 handleWheel: function(e) {
1110 delta = e.wheelDelta/120;
1114 } else if (e.detail) {
1115 delta = -e.detail/3;
1127 handleKeys: function(e) {
1129 if (/^(input|textarea)$/i.test(e.target.nodeName)) return;
1131 switch (e.keyCode) {
1132 case 37: // left arrow
1134 case 39: // right arrow
1138 this.showNotes(); break;
1140 this.switch3D(); break;
1144 handleTouchStart: function(e) {
1145 this._touchStartX = e.touches[0].pageX;
1147 handleTouchEnd: function(e) {
1148 var delta = this._touchStartX - e.changedTouches[0].pageX;
1149 var SWIPE_SIZE = 150;
1150 if (delta > SWIPE_SIZE) {
1152 } else if (delta< -SWIPE_SIZE) {
1159 var slideshow = new SlideShow(query('.slide'));
1165 document.querySelector('#toggle-counter').addEventListener('click', toggleCounter, false);
1166 document.querySelector('#toggle-size').addEventListener('click', toggleSize, false);
1167 document.querySelector('#toggle-transitions').addEventListener('click', toggleTransitions, false);
1168 document.querySelector('#toggle-gradients').addEventListener('click', toggleGradients, false);
1171 var counters = document.querySelectorAll('.counter');
1172 var slides = document.querySelectorAll('.slide');
1174 function toggleCounter() {
1175 toArray(counters).forEach(function(el) {
1176 el.style.display = (el.offsetHeight) ? 'none' : 'block';
1180 function toggleSize() {
1181 toArray(slides).forEach(function(el) {
1182 if (!/reduced/.test(el.className)) {
1183 addClass(el, 'reduced');
1186 removeClass(el, 'reduced');
1191 function toggleTransitions() {
1192 toArray(slides).forEach(function(el) {
1193 if (!/no-transitions/.test(el.className)) {
1194 addClass(el, 'no-transitions');
1197 removeClass(el, 'no-transitions');
1202 function toggleGradients() {
1203 toArray(slides).forEach(function(el) {
1204 if (!/no-gradients/.test(el.className)) {
1205 addClass(el, 'no-gradients');
1208 removeClass(el, 'no-gradients');
1225 src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js">
1227 <script>CFInstall.check({ mode: "overlay" });</script>