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
697 <p>show memory usage</p>
700 PID Vss Rss Pss Uss cmdline
701 142 37328K 27332K 16926K 15472K system_server
702 218 17568K 17564K 7469K 6228K com.android.phone
703 220 16040K 16040K 5694K 4380K org.zeam.core
704 1783 18276K 15880K 5308K 3836K com.android.term
705 298 15172K 15164K 4905K 3636K com.android.mms
706 1693 14860K 14860K 4200K 2724K com.android.music
707 441 13260K 13260K 3752K 2616K com.als.usagetimelines
708 209 13052K 13052K 3329K 2188K com.access_company.graffiti
709 1700 13300K 13300K 3242K 1904K android.process.media
710 107 11860K 11860K 2118K 900K zygote
711 108 768K 768K 342K 300K /system/bin/mediaserver
712 1826 488K 484K 296K 280K procrank
722 <h1>Do we need FLOSS app market?</h1>
725 <a href="http://lwn.net/Articles/373128/">Jonathan Corbet of LWM fame</a> think we do and I agree.
727 <h2>Working idea</h2>
729 <ul class="bulleted">
730 <li>FLOSS licence</li>
731 <li>source code repository (git)</li>
732 <li>all source searchable (gitweb?)</li>
733 <li>automatic builds (x86 NDK?)</li>
734 <li><b>download links from web!</b></li>
735 <li>backup apps to cloud (CouchDB?)</li>
738 <p>Anybody interested in something like that?</p>
744 <!-- extra slides -->
747 <section class="middle">
748 <h2>The Difference Between Android And iPhone</h2>
749 <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>
754 <section class="middle">
756 <img src="commic-Android.png">
761 <section class="middle">
763 <img src="commic-iPhone.png">
768 <section class="middle">
769 <h1>Questions? Comments?</h1>
770 <h2>More information</h2>
771 <p>this presentation: <a href="http://dpavlin.github.com/android-command-line/">http://dpavlin.github.com/android-command-line/</a></p>
772 <p>repository: <a href="http://github.com/dpavlin/android-command-line/">http://github.com/dpavlin/android-command-line/</a></p>
773 <p>my blog: <a href="http://blog.rot13.org">http://dpavlin.github.com/android-command-line/</a></p>
779 <section class="middle">
784 <section class="middle">
785 <h2>Slides template</h2>
786 <p>Click here, Press <span class="key">→</span> key to advance.</p>
792 <section class="middle">
797 <li>[arrow keys] to go next and previous</p></li>
798 <li>[mouse scroll wheel] to go next and previous</p></li>
799 <li>[Ctrl or Command] + [+/-] to zoom in and out</p></li>
800 <li>[touch gestures] for mobile devices</p></li>
806 <section class="middle">
811 <li><input type="checkbox" id="toggle-size" /><label for="toggle-size"><span>Grow</span></label></li>
812 <li><input type="checkbox" id="toggle-transitions" checked /><label for="toggle-transitions"><span>Transitions</span></label></li>
813 <li><input type="checkbox" id="toggle-gradients" checked /><label for="toggle-gradients"><span>Gradients</span></label></li>
814 <li><input type="checkbox" id="toggle-counter" checked /><label for="toggle-counter"><span>Slide numbers</span></label></li>
824 <h2>Placeholder content</h2>
826 <pre>// some sample code
827 <canvas id="canvas" width="838" height="220"></canvas>
830 var canvasContext = document.getElementById("canvas").<b>getContext</b>("2d");
831 canvasContext.<b>fillRect</b>(250, 25, 150, 100);
833 canvasContext.<b>beginPath</b>();
834 canvasContext.<b>arc</b>(450, 110, 100, Math.PI * 1/2, Math.PI * 3/2);
835 canvasContext.<b>lineWidth</b> = 15;
836 canvasContext.<b>lineCap</b> = 'round';
837 canvasContext.<b>strokeStyle</b> = 'rgba(255, 127, 0, 0.5)';
838 canvasContext.<b>stroke</b>();
841 <ul class="bulleted">
851 </div> <!-- slides -->
853 </div> <!-- presentation -->
858 var disableBuilds = true;
861 var spaces = /\s+/, a1 = [''];
863 var toArray = function(list) {
864 return Array.prototype.slice.call(list || [], 0);
867 var byId = function(id) {
868 if (typeof id == 'string') { return doc.getElementById(id); }
872 var query = function(query, root) {
873 if (!query) { return []; }
874 if (typeof query != 'string') { return toArray(query); }
875 if (typeof root == 'string') {
877 if(!root){ return []; }
880 root = root || document;
881 var rootIsDoc = (root.nodeType == 9);
882 var doc = rootIsDoc ? root : (root.ownerDocument || document);
884 // rewrite the query to be ID rooted
885 if (!rootIsDoc || ('>~+'.indexOf(query.charAt(0)) >= 0)) {
886 root.id = root.id || ('qUnique' + (ctr++));
887 query = '#' + root.id + ' ' + query;
889 // don't choke on something like ".yada.yada >"
890 if ('>~+'.indexOf(query.slice(-1)) >= 0) { query += ' *'; }
892 return toArray(doc.querySelectorAll(query));
895 var strToArray = function(s) {
896 if (typeof s == 'string' || s instanceof String) {
897 if (s.indexOf(' ') < 0) {
901 return s.split(spaces);
907 var addClass = function(node, classStr) {
908 classStr = strToArray(classStr);
909 var cls = ' ' + node.className + ' ';
910 for (var i = 0, len = classStr.length, c; i < len; ++i) {
912 if (c && cls.indexOf(' ' + c + ' ') < 0) {
916 node.className = cls.trim();
919 var removeClass = function(node, classStr) {
921 if (classStr !== undefined) {
922 classStr = strToArray(classStr);
923 cls = ' ' + node.className + ' ';
924 for (var i = 0, len = classStr.length; i < len; ++i) {
925 cls = cls.replace(' ' + classStr[i] + ' ', ' ');
931 if (node.className != cls) {
932 node.className = cls;
936 var toggleClass = function(node, classStr) {
937 var cls = ' ' + node.className + ' ';
938 if (cls.indexOf(' ' + classStr.trim() + ' ') >= 0) {
939 removeClass(node, classStr);
941 addClass(node, classStr);
945 var ua = navigator.userAgent;
946 var isFF = parseFloat(ua.split('Firefox/')[1]) || undefined;
947 var isWK = parseFloat(ua.split('WebKit/')[1]) || undefined;
948 var isOpera = parseFloat(ua.split('Opera/')[1]) || undefined;
950 var canTransition = (function() {
951 var ver = parseFloat(ua.split('Version/')[1]) || undefined;
952 // test to determine if this browser can handle CSS transitions.
953 var cachedCanTransition =
954 (isWK || (isFF && isFF > 3.6 ) || (isOpera && ver >= 10.5));
955 return function() { return cachedCanTransition; }
961 var Slide = function(node, idx) {
964 this._count = idx + 1;
967 addClass(this._node, 'slide distant-slide');
970 this._makeBuildList();
979 _states: [ 'distant-slide', 'far-past',
980 'past', 'current', 'future',
981 'far-future', 'distant-slide' ],
982 setState: function(state) {
983 if (typeof state != 'string') {
984 state = this._states[state];
986 if (state == 'current' && !this._visited) {
987 this._visited = true;
988 this._makeBuildList();
990 removeClass(this._node, this._states);
991 addClass(this._node, state);
992 this._currentState = state;
994 // delay first auto run. Really wish this were in CSS.
999 setTimeout(function(){ _t._runAutos(); } , 400);
1001 _makeCounter: function() {
1002 if(!this._count || !this._node) { return; }
1003 var c = doc.createElement('span');
1004 c.innerHTML = this._count;
1005 c.className = 'counter';
1006 this._node.appendChild(c);
1008 _makeBuildList: function() {
1009 this._buildList = [];
1010 if (disableBuilds) { return; }
1012 this._buildList = query('[data-build] > *', this._node);
1014 this._buildList.forEach(function(el) {
1015 addClass(el, 'to-build');
1018 _runAutos: function() {
1019 if (this._currentState != 'current') {
1022 // find the next auto, slice it out of the list, and run it
1024 this._buildList.some(function(n, i) {
1025 if (n.hasAttribute('data-auto')) {
1032 var elem = this._buildList.splice(idx, 1)[0];
1033 var transitionEnd = isWK ? 'webkitTransitionEnd' : (isFF ? 'mozTransitionEnd' : 'oTransitionEnd');
1035 if (canTransition()) {
1036 var l = function(evt) {
1037 elem.parentNode.removeEventListener(transitionEnd, l, false);
1040 elem.parentNode.addEventListener(transitionEnd, l, false);
1041 removeClass(elem, 'to-build');
1043 setTimeout(function() {
1044 removeClass(elem, 'to-build');
1050 buildNext: function() {
1051 if (!this._buildList.length) {
1054 removeClass(this._buildList.shift(), 'to-build');
1062 var SlideShow = function(slides) {
1063 this._slides = (slides || []).map(function(el, idx) {
1064 return new Slide(el, idx);
1067 var h = window.location.hash;
1069 this.current = parseInt(h.split('#slide')[1], 10);
1070 }catch (e) { /* squeltch */ }
1071 this.current = isNaN(this.current) ? 1 : this.current;
1073 doc.addEventListener('keydown',
1074 function(e) { _t.handleKeys(e); }, false);
1075 doc.addEventListener('mousewheel',
1076 function(e) { _t.handleWheel(e); }, false);
1077 doc.addEventListener('DOMMouseScroll',
1078 function(e) { _t.handleWheel(e); }, false);
1079 doc.addEventListener('touchstart',
1080 function(e) { _t.handleTouchStart(e); }, false);
1081 doc.addEventListener('touchend',
1082 function(e) { _t.handleTouchEnd(e); }, false);
1083 window.addEventListener('popstate',
1084 function(e) { _t.go(e.state); }, false);
1088 SlideShow.prototype = {
1090 _update: function(dontPush) {
1091 document.querySelector('#presentation-counter').innerText = this.current;
1092 if (history.pushState) {
1094 history.pushState(this.current, 'Slide ' + this.current, '#slide' + this.current);
1097 window.location.hash = 'slide' + this.current;
1099 for (var x = this.current-1; x < this.current + 7; x++) {
1100 if (this._slides[x-4]) {
1101 this._slides[x-4].setState(Math.max(0, x-this.current));
1108 if (!this._slides[this.current-1].buildNext()) {
1109 this.current = Math.min(this.current + 1, this._slides.length);
1114 this.current = Math.max(this.current-1, 1);
1118 if (history.pushState && this.current != num) {
1119 history.replaceState(this.current, 'Slide ' + this.current, '#slide' + this.current);
1126 showNotes: function() {
1127 var isOn = this._notesOn = !this._notesOn;
1128 query('.notes').forEach(function(el) {
1129 el.style.display = (notesOn) ? 'block' : 'none';
1132 switch3D: function() {
1133 toggleClass(document.body, 'three-d');
1135 handleWheel: function(e) {
1138 delta = e.wheelDelta/120;
1142 } else if (e.detail) {
1143 delta = -e.detail/3;
1155 handleKeys: function(e) {
1157 if (/^(input|textarea)$/i.test(e.target.nodeName)) return;
1159 switch (e.keyCode) {
1160 case 37: // left arrow
1162 case 39: // right arrow
1166 this.showNotes(); break;
1168 this.switch3D(); break;
1172 handleTouchStart: function(e) {
1173 this._touchStartX = e.touches[0].pageX;
1175 handleTouchEnd: function(e) {
1176 var delta = this._touchStartX - e.changedTouches[0].pageX;
1177 var SWIPE_SIZE = 150;
1178 if (delta > SWIPE_SIZE) {
1180 } else if (delta< -SWIPE_SIZE) {
1187 var slideshow = new SlideShow(query('.slide'));
1193 document.querySelector('#toggle-counter').addEventListener('click', toggleCounter, false);
1194 document.querySelector('#toggle-size').addEventListener('click', toggleSize, false);
1195 document.querySelector('#toggle-transitions').addEventListener('click', toggleTransitions, false);
1196 document.querySelector('#toggle-gradients').addEventListener('click', toggleGradients, false);
1199 var counters = document.querySelectorAll('.counter');
1200 var slides = document.querySelectorAll('.slide');
1202 function toggleCounter() {
1203 toArray(counters).forEach(function(el) {
1204 el.style.display = (el.offsetHeight) ? 'none' : 'block';
1208 function toggleSize() {
1209 toArray(slides).forEach(function(el) {
1210 if (!/reduced/.test(el.className)) {
1211 addClass(el, 'reduced');
1214 removeClass(el, 'reduced');
1219 function toggleTransitions() {
1220 toArray(slides).forEach(function(el) {
1221 if (!/no-transitions/.test(el.className)) {
1222 addClass(el, 'no-transitions');
1225 removeClass(el, 'no-transitions');
1230 function toggleGradients() {
1231 toArray(slides).forEach(function(el) {
1232 if (!/no-gradients/.test(el.className)) {
1233 addClass(el, 'no-gradients');
1236 removeClass(el, 'no-gradients');
1253 src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js">
1255 <script>CFInstall.check({ mode: "overlay" });</script>